General Setup

Setup chunk

Setup reticulate

knitr::opts_chunk$set(fig.width = 8)
knitr::opts_knit$set(root.dir = normalizePath(".."))
knitr::opts_knit$get("root.dir")
[1] "/nas/groups/treutlein/USERS/tomasgomes/projects/liver_regen"

Load libraries

library(Seurat)
library(ggplot2)
library(ggridges)
library(dplyr)
library(destiny)
library(fdrtool)
library(gam)
library(gamclass)
library(dtw)
library(data.table)
library(clusterProfiler)
library(ReactomePA)

Function for cross-validation

CVgam = function (formula, data, nfold = 10, debug.level = 0, method = "glm.fit", 
    printit = TRUE, cvparts = NULL, gamma = 1, seed = 29){
  # modified from the `gamclass` package to use gam::gam
    if (is.null(cvparts)) {
        set.seed(seed)
        cvparts <- sample(1:nfold, nrow(data), replace = TRUE)
    }
    folds <- unique(cvparts)
    khat <- hat <- numeric(nrow(data))
    scale.gam <- summary(gam::gam(formula, data = data, method = method))$scale
    for (i in folds) {
        trainrows <- cvparts != i
        testrows <- cvparts == i
        elev.gam <- gam::gam(formula, data = data[trainrows, ], method = method, 
            gamma = gamma)
        hat[testrows] <- predict(elev.gam, newdata = data[testrows, 
            ], select = TRUE)
        res <- residuals(elev.gam)
    }
    y <- eval(formula[[2]], envir = as.data.frame(data))
    res <- y - hat
    cvscale <- sum(res^2)/length(res)
    prntvec <- c(GAMscale = scale.gam, `CV-mse-GAM ` = cvscale)
    if (printit) 
        print(round(prntvec, 4))
    invisible(list(fitted = hat, resid = res, cvscale = cvscale, 
        scale.gam = scale.gam))
}

Load data

Load data (from all cells)

CVgam = function (formula, data, nfold = 10, debug.level = 0, method = "glm.fit", 
    printit = TRUE, cvparts = NULL, gamma = 1, seed = 29){
  # modified from the `gamclass` package to use gam::gam
    if (is.null(cvparts)) {
        set.seed(seed)
        cvparts <- sample(1:nfold, nrow(data), replace = TRUE)
    }
    folds <- unique(cvparts)
    khat <- hat <- numeric(nrow(data))
    scale.gam <- summary(gam::gam(formula, data = data, method = method))$scale
    for (i in folds) {
        trainrows <- cvparts != i
        testrows <- cvparts == i
        elev.gam <- gam::gam(formula, data = data[trainrows, ], method = method, 
            gamma = gamma)
        hat[testrows] <- predict(elev.gam, newdata = data[testrows, 
            ], select = TRUE)
        res <- residuals(elev.gam)
    }
    y <- eval(formula[[2]], envir = as.data.frame(data))
    res <- y - hat
    cvscale <- sum(res^2)/length(res)
    prntvec <- c(GAMscale = scale.gam, `CV-mse-GAM ` = cvscale)
    if (printit) 
        print(round(prntvec, 4))
    invisible(list(fitted = hat, resid = res, cvscale = cvscale, 
        scale.gam = scale.gam))
}

Hepatocytes

Subset and process each condition

allcells_css = readRDS(file = "data/processed/allcells_css.RDS")

Healthy analysis

For healthy cells (which will serve as reference), do additional filtering and reprocessing

DimPlot(hep_cells$healthy, reduction = "umap", group.by = "unique_name")
outcells = (hep_cells$healthy@reductions$umap@cell.embeddings[,1]>(1) &
              hep_cells$healthy@reductions$umap@cell.embeddings[,2]>5)
hep_cells$healthy = hep_cells$healthy[,!outcells]

hep_cells$healthy = suppressWarnings(SCTransform(hep_cells$healthy, do.correct.umi = T, 
                                                 verbose = F,
                                                 vars.to.regress = c("unique_name","nCount_RNA"),
                                                 variable.features.rv.th = 1, seed.use = 1,
                                                 return.only.var.genes = F, 
                                                 variable.features.n = NULL))
hep_cells$healthy = RunPCA(hep_cells$healthy, verbose = F)
hep_cells$healthy = RunUMAP(hep_cells$healthy, dims = 1:15, verbose = F)
DimPlot(hep_cells$healthy, reduction = "umap", group.by = "unique_name")

hep_cells$healthy = FindNeighbors(hep_cells$healthy, reduction = "pca", dims = 1:15,
                                  force.recalc = T, graph.name = "hep")
hep_cells$healthy = FindClusters(hep_cells$healthy, graph.name = "hep", algorithm = 4, 
                                 resolution = seq(0.1,0.8,0.1), verbose = F)
DimPlot(hep_cells$healthy, reduction = "umap", group.by = "hep_res.0.3")
hep_cells$healthy = SetIdent(hep_cells$healthy, value = "hep_res.0.6")
mk_hep_h = FindAllMarkers(hep_cells$healthy, assay = "SCT")

saveRDS(hep_cells$healthy, file = "results/zonation_healthy/hep_healthy_srat.RDS")

Save hepatocytes

saveRDS(hep_cells, file="results/zonation_cond/hep_cells.RDS")

Plot expression of zonation markers

gfresh = c("CYP1A2", "CYP2E1", "GLUL", "CYP3A4", "G0S2", "APOC1", "UGT2B17", "BCHE", # central 
           "SAA1", "HAMP", "SAA2", "CPS1", "C3", "C1R", "MT-ND4", "MT-CO2") # portal

png("results/zonation_healthy/healthy_hepatocyte_zonation_markers_violin.png", 
    height = 1400, width = 1400)
VlnPlot(hep_cells$healthy, features = gfresh, group.by = "names_clusters", 
        pt.size = 0, sort = "increasing")
dev.off()
png("results/zonation_healthy/healthy_hepatocyte_zonation_markers_umap.png", 
    height = 1400, width = 1400)
FeaturePlot(hep_cells$healthy, features = gfresh, pt.size = 0.6)
dev.off()

Obtain Diffusion maps projection

set.seed(1)
dm = DiffusionMap(hep_cells$healthy@reductions$pca@cell.embeddings[,1:4], 
                  rotate = T, n_eigs = 5)
dpt = DPT(dm)
plot(dpt)

Check expression of some genes

dpt_flat <- branch_divide(dpt, integer(0L))
root = min(dpt_flat@branch[, 1], na.rm = TRUE)
dpt_vals = destiny:::dpt_for_branch(dpt_flat, root)
df_dc = cbind(hep_cells$healthy@meta.data, 
              data.frame("DC1" = dpt@dm$DC1, "DC2" = dpt@dm$DC2, "DPT" = dpt_vals),
              Matrix::t(hep_cells$healthy@assays$SCT@data[gfresh,]))
colnames(df_dc) = gsub("-", ".", colnames(df_dc), fixed = T)

plt_list = list()
for(n in c(gsub("-", ".", gfresh, fixed = T), "names_clusters", "unique_name")){
  plt_list[[n]] = ggplot(df_dc, aes_string(x = "DC1", y = "DC2", colour = n))+
    geom_point()+
    theme_classic()
}

png("results/zonation_healthy/healthy_hepatocyte_zonation_markers_dc.png", 
    height = 900, width = 2800)
cowplot::plot_grid(plotlist = plt_list, nrow = 3, ncol = 6, align = "hv")
dev.off()

We’ll be using DC1 as the pseudotime. This was the best approximation possible to the zonation gradient based on diffusion maps

dpt_flat <- branch_divide(dpt, integer(0L))
root = min(dpt_flat@branch[, 1], na.rm = TRUE)
dpt_vals = destiny:::dpt_for_branch(dpt_flat, root)
df_dc = cbind(hep_cells$healthy@meta.data, 
              data.frame("DC1" = dpt@dm$DC1, "DC2" = dpt@dm$DC2, "DPT" = dpt_vals),
              Matrix::t(hep_cells$healthy@assays$SCT@data[gfresh,]))
colnames(df_dc) = gsub("-", ".", colnames(df_dc), fixed = T)

plt_list = list()
for(n in c(gsub("-", ".", gfresh, fixed = T), "names_clusters", "unique_name")){
  plt_list[[n]] = ggplot(df_dc, aes_string(x = "DC1", y = "DC2", colour = n))+
    geom_point()+
    theme_classic()
}

png("results/zonation_healthy/healthy_hepatocyte_zonation_markers_dc.png", 
    height = 900, width = 2800)
cowplot::plot_grid(plotlist = plt_list, nrow = 3, ncol = 6, align = "hv")
dev.off()
null device 
          1 

Find genes varying along the pseudotime

pdf("results/zonation_healthy/hep_distributions_dc1.pdf", useDingbats = F, 
    height = 4, width = 9)
plt1 = ggplot(df_dc, aes(x = DC1, y = names_clusters, 
                  group = names_clusters, fill = names_clusters))+
  geom_density_ridges2(alpha = 0.4)+
  theme_bw()+
  theme(legend.position = "none")

plt2 = ggplot(df_dc, aes(x = DC1, y = unique_name, 
                  group = unique_name, fill = unique_name))+
  geom_density_ridges2(alpha = 0.4)+
  theme_bw()+
  theme(legend.position = "none")

cowplot::plot_grid(plt1, plt2, ncol = 2, align = "h")
Picking joint bandwidth of 0.00354
Picking joint bandwidth of 0.00457
dev.off()
null device 
          1 

Analysis of other conditions

Now use the top genes to learn the pseudozonation coordinates and project the embolised and regenerating data

# will only use variable genes
hvg = hep_cells$healthy@assays$SCT@var.features

# Fit GAM for each gene using pseudotime as independent variable.
t = df_dc$DC1
t = scales::rescale(rank(df_dc$DC1), c(0.00001, 0.99999))
gene_fit_p = c()
gene_fit_vals = data.frame(row.names = 1:100)
for(i in 1:length(hvg)){
  g = hvg[i]
  z = hep_cells$healthy@assays$SCT@data[g,]
  
  d = data.frame(z=z, t=t)
  tmp = suppressMessages(gam(z ~ ns(t, df = 3), data=d))
  
  # bins for model fitting
  bb = seq(min(t), max(t), length.out = 100)
  gene_fit_vals[,g] = suppressMessages(predict(tmp,
                                               newdata = data.frame(t = bb)))
  p = summary(tmp)$parametric.anova$`Pr(>F)`[1]
  gene_fit_p = c(gene_fit_p, p)
  names(gene_fit_p)[length(gene_fit_p)] = g
}

gene_fit_p = fdrtool::fdrtool(gene_fit_p, statistic="pvalue", plot=F, 
                              verbose=F, cutoff.method="pct0", pct0=0.9)$qval

Calculate correlations

sig_genes = gene_fit_p[gene_fit_p<=0.05]
top_sig_genes = names(sig_genes[order(sig_genes, decreasing = F)][1:1000])
top_sig_genes = top_sig_genes[top_sig_genes %in% rownames(hep_cells$embolised@assays$SCT@data) &
                                top_sig_genes %in%  rownames(hep_cells$regenerating@assays$SCT@data)]

data_gam_h = cbind(data.frame("DC1" = df_dc$DC1),
                   Matrix::t(hep_cells$health@assays$SCT@data[top_sig_genes,]))
#data_gam_h$DC1 = scales::rescale(-data_gam_h$DC1, c(0.00001, 0.99999))
data_gam_h$DC1 = scales::rescale(rank(data_gam_h$DC1), c(0.00001, 0.99999))
#bins_dc1 = cut(data_gam_h$DC1, 200)
#cells_use = unlist(tapply(1:length(bins_dc1), bins_dc1, function(x) sample(x, 12)))

gam_healthy = gam::gam(DC1 ~ ., data = data_gam_h, family = mgcv::betar(link = "logit", eps = 0.00001))
#gam_healthy = gam::gam(DC1 ~ ., data = data_gam_h)

hea_pred = predict(gam_healthy,
                   Matrix::t(hep_cells$healthy@assays$SCT@data[top_sig_genes,]), type = "response")
reg_pred = predict(gam_healthy,
                   Matrix::t(hep_cells$regenerating@assays$SCT@data[top_sig_genes,]), type = "response")
emb_pred = predict(gam_healthy,
                   Matrix::t(hep_cells$embolised@assays$SCT@data[top_sig_genes,]), type = "response")

plot(density(reg_pred), col = "salmon", ylim = c(0,3.25))
lines(density(emb_pred), col = "darkred")
lines(density(data_gam_h$DC1), col = "orange")
lines(density(hea_pred), col = "grey50")
legend(-0.073,32, legend = c("healthy (DC1)", "healthy (pred)", 
                         "embolised (pred)", "regenerating (pred)"),
       fill = c("green", "grey50", "red", "blue"))


plot_df = data.frame(vals = c(data_gam_h$DC1, emb_pred, reg_pred), 
                     Condition = c(rep("healthy", length(data_gam_h$DC1)), 
                                   rep("embolised", length(emb_pred)), 
                                   rep("regenerating", length(reg_pred))),
                     Donor = c(hep_cells$healthy$Donor, 
                               hep_cells$embolised$Donor, hep_cells$regenerating$Donor))
plot_df$bins100 = cut(plot_df$vals, 10)
plot_df$Condition = factor(plot_df$Condition, levels = c("healthy", "embolised", "regenerating"))
ggplot(plot_df, aes(x = bins100, fill = Condition))+
     geom_bar(position = "fill")+
     labs(x = "zonation (binned)", y = "proportion")+
     scale_y_continuous(expand = c(0,0))+
     scale_fill_manual(values = colcond)+
     guides(fill = guide_legend(title.position = "top"))+
     theme(axis.text.x = element_blank(),
           legend.title.align = 0,
           legend.position = "bottom")

ggplot(plot_df, aes(x = bins100, fill = Condition))+
     facet_wrap(~Condition)+
     geom_bar()+
     labs(x = "zonation (binned)", y = "Number of cells")+
     scale_y_continuous(expand = c(0,0))+
     scale_fill_manual(values = colcond)+
     coord_flip()+
     guides(fill = guide_legend(title.position = "top"))+
     theme(axis.text.y = element_blank(),
           legend.title.align = 0,
           legend.position = "bottom")

ggplot(plot_df, aes(x = vals, colour = Donor))+
     facet_wrap(~Condition)+
     geom_density()+
     labs(x = "zonation (binned)", y = "proportion")+
     theme_bw()+
     theme(legend.title.align = 0,
           legend.position = "bottom")


# data_gam_h$bins20 = cut(data_gam_h$DC1, 20)
# mean_bins = apply(data_gam_h[,2:501], 2, function(x) tapply(x, data_gam_h$bins20, mean))
# cor_reg_bins = cor(as.matrix(hep_cells$regenerating@assays$SCT@data[colnames(mean_bins),]), 
#                    t(mean_bins), method = "sp")
# max_cor_regen = apply(cor_reg_bins, 1, which.max)
# testdf = data.frame(bins = max_cor_regen, dons = hep_cells$regenerating$Donor)
# ggplot(testdf, aes(x = bins, fill = dons))+geom_bar()+facet_wrap(~dons, scales = "free_y")

reg_df = data.frame("pt" = reg_pred, "don" = hep_cells$regenerating$Donor)
reg_df = cbind(reg_df, Matrix::t(hep_cells$regenerating@assays$SCT@data[gfresh,]))
ggplot(reg_df, aes(x = pt, y = SAA1, colour = don))+
  geom_point()+
  theme_classic()+
  facet_wrap(~don)+
  geom_hline(yintercept = 3)+
  geom_vline(xintercept = c(0,1))


ggplot(reg_df, aes(x = SAA2, y = GLUL, colour = don))+
  geom_point()+
  theme_classic()+
  facet_wrap(~don)


traj_list = list("healthy" = data_gam_h$DC1, "embolised" = emb_pred, "regenerating" = reg_pred)

Find varying genes - using all cells along the ranked pseudotime

cor_h = t(cor(traj_list$healthy, as.matrix(Matrix::t(hep_cells$healthy@assays$SCT@data)),
              method = "sp"))
cor_emb = t(cor(traj_list$embolised, as.matrix(Matrix::t(hep_cells$embolised@assays$SCT@data)),
                method = "sp"))
the standard deviation is zero
cor_reg = t(cor(traj_list$regenerating, as.matrix(Matrix::t(hep_cells$regenerating@assays$SCT@data)),
                method = "sp"))
the standard deviation is zero
l = list(cor_h, cor_emb, cor_reg)
all_corr = Reduce(function(x, y) merge(x,y, by = "rn", all = T), 
                  lapply(l, function(x) data.frame(x, rn = row.names(x))))
colnames(all_corr) = c("genes", "healthy", "embolised", "regenerating")

write.csv(all_corr, file = paste0("results/zonation_cond/hep_correlations_zonation_rank.csv"),
          col.names = T, row.names = F, quote = F)
attempt to set 'col.names' ignored

Save trajectories and genes

qvals_list = list()
fits_list = list()

# get HVG from all conditions
hvg = unique(c(hep_cells[["healthy"]]@assays$SCT@var.features,
               hep_cells[["embolised"]]@assays$SCT@var.features,
               hep_cells[["regenerating"]]@assays$SCT@var.features))
hvg = hvg[hvg %in% rownames(hep_cells[["healthy"]]@assays$SCT@data) &
            hvg %in% rownames(hep_cells[["embolised"]]@assays$SCT@data) &
            hvg %in% rownames(hep_cells[["regenerating"]]@assays$SCT@data)]

for(cond in names(hep_cells)){
  print(cond)
  # Fit GAM for each gene using pseudotime as independent variable.
  t = traj_list[[cond]]
  gene_fit_p = c()
  gene_fit_vals = data.frame(row.names = 1:100)
  for(i in 1:length(hvg)){
    g = hvg[i]
    z = hep_cells[[cond]]@assays$SCT@data[g,]
    
    d = data.frame(z=z, t=t)
    tmp = suppressMessages(gam(z ~ ns(t, df = 3), data=d))
    
    # bins for model fitting
    bb = seq(min(t), max(t), length.out = 100)
    gene_fit_vals[,g] = suppressMessages(predict(tmp,
                                                 newdata = data.frame(t = bb)))
    p = summary(tmp)$parametric.anova$`Pr(>F)`[1]
    gene_fit_p = c(gene_fit_p, p)
    names(gene_fit_p)[length(gene_fit_p)] = g
  }
  
  if(sum(is.na(gene_fit_p))>0){
    gene_fit_p[is.na(gene_fit_p)] = 1
  }
  gene_fit_p = fdrtool::fdrtool(gene_fit_p, statistic="pvalue", plot=F, 
                                verbose=F, cutoff.method="pct0", pct0=0.9)$qval
  
  qvals_list[[cond]] = gene_fit_p
  fits_list[[cond]] = gene_fit_vals
}
[1] "healthy"
[1] "embolised"
[1] "regenerating"

Find varying genes - normalised by bins along the ranked pseudotime

save(fits_list, qvals_list, traj_list, 
     file = "results/zonation_cond/hep_traj_qval_fits_rank.RData")

Save trajectories and genes

save(fits_b_list, qvals_b_list, traj_list, 
     file = "results/zonation_cond/hep_binned_traj_qval_fits_rank.RData")

Find varying genes - using each donor as a replicate and calculating the average expression for each of 100 bins

save(fits_b_list, qvals_b_list, traj_list, 
     file = "results/zonation_cond/hep_binned_traj_qval_fits_rank.RData")

Save trajectories and genes

save(fits_db_list, qvals_db_list, traj_list, 
     file = "results/zonation_cond/hep_binnedDonor_traj_qval_fits_rank.RData")

Plot cell distributions in pseudotime

plot_df = data.frame("pseudotime" = c(traj_list$healthy, traj_list$embolised,
                                      traj_list$regenerating),
           "donor" = c(hep_cells$healthy@meta.data[,"Donor"],
                       hep_cells$embolised@meta.data[names(traj_list$embolised),"Donor"],
                       hep_cells$regenerating@meta.data[names(traj_list$regenerating),"Donor"]),
           "cond" = c(rep("healthy", length(traj_list$healthy)), 
                      rep("embolised", length(traj_list$embolised)),
                      rep("regenerating", length(traj_list$regenerating))))

pdf("results/zonation_cond/hep_cell_distributions_zonation.pdf", useDingbats = F, 
    width = 6, height = 5)
ggplot(plot_df, aes(x = donor, y = pseudotime, fill = cond))+
  geom_boxplot(notch = T)+
  theme_bw()

ggplot(plot_df, aes(x = pseudotime, group = donor, colour = donor))+
  facet_wrap(~cond)+
  geom_density()+
  theme_bw()

ggplot(plot_df, aes(x = pseudotime, colour = cond))+
  geom_density()+
  theme_bw()
dev.off()

Do cross validation on the healthy trajectory

plot_df = data.frame("pseudotime" = c(traj_list$healthy, traj_list$embolised,
                                      traj_list$regenerating),
           "donor" = c(hep_cells$healthy@meta.data[,"Donor"],
                       hep_cells$embolised@meta.data[names(traj_list$embolised),"Donor"],
                       hep_cells$regenerating@meta.data[names(traj_list$regenerating),"Donor"]),
           "cond" = c(rep("healthy", length(traj_list$healthy)), 
                      rep("embolised", length(traj_list$embolised)),
                      rep("regenerating", length(traj_list$regenerating))))

pdf("results/zonation_cond/hep_cell_distributions_zonation.pdf", useDingbats = F, 
    width = 6, height = 5)
ggplot(plot_df, aes(x = donor, y = pseudotime, fill = cond))+
  geom_boxplot(notch = T)+
  theme_bw()

ggplot(plot_df, aes(x = pseudotime, group = donor, colour = donor))+
  facet_wrap(~cond)+
  geom_density()+
  theme_bw()

ggplot(plot_df, aes(x = pseudotime, colour = cond))+
  geom_density()+
  theme_bw()
dev.off()
null device 
          1 

Save zonation results

sig_genes = qvals_list$healthy[qvals_list$healthy<=0.05]
top_sig_genes = names(sig_genes[order(sig_genes, decreasing = F)][1:500]) 

data_gam_h = cbind(data.frame("DC1" = traj_list$healthy),
                   Matrix::t(hep_cells$health@assays$SCT@data[top_sig_genes,]))
colnames(data_gam_h) = gsub("-", "_", colnames(data_gam_h), fixed = T)
colnames(data_gam_h) = gsub(".", "_", colnames(data_gam_h), fixed = T)

cv_gam = CVgam(formula = DC1 ~ ., data = data_gam_h, nfold = 10, seed = 1, method = "glm.fit")
CV-mse-GAM  
     0.0106 
pdf("results/zonation_healthy/hep_original_fitted_pseudotime.pdf", useDingbats = F, 
    width = 6, height = 5)
plot(data_gam_h$DC1, cv_gam$fitted, xlab = "Healthy pseudotime (DC1)",ylab = "Predicted Healthy",
     main = paste0("Healthy original vs fitted pseudotime\nMSE: ", round(cv_gam$cvscale, 6)),
     pch = 19, cex = 0.5)
abline(0,1, col = "blue", lwd = 3)
dev.off()
null device 
          1 

Endothelial cells

Subset and process each condition (only LSEC)

df_h = data.table("zonation_pt" = traj_list$healthy)
df_h$zonation_int = cut(df_h$zonation_pt, 10)

df_e = data.table("zonation_pt" = traj_list$embolised)
df_e = df_h[df_e, on="zonation_pt", roll=Inf, rollends = T]

df_r = data.table("zonation_pt" = traj_list$regenerating)
df_r = df_h[df_r, on="zonation_pt", roll=Inf, rollends = T]
Error in colnamesInt(i, unname(on), check_dups = FALSE) : 
  argument specifying columns specify non existing column(s): cols[1]='zonation_pt'

Healthy analysis

For healthy cells (which will serve as reference), do additional filtering and reprocessing

DimPlot(end_cells$healthy, reduction = "umap", group.by = "allcells_clusters")
gfresh = c("CLEC1B", "CLEC4G", "LYVE1", "CD14",  # central 
           "PECAM1", "AQP1", "VWF", "CD34", # portal
           "ALB", "MKI67", "TRAC", "DCN") 
FeaturePlot(end_cells$healthy, features = gfresh, reduction = "umap")

outcells = (end_cells$healthy@reductions$umap@cell.embeddings[,1]<(-4)) # this removes what seem to be central hepatocytes
end_cells$healthy = end_cells$healthy[,!outcells]

end_cells$healthy = suppressWarnings(SCTransform(end_cells$healthy, do.correct.umi = T, 
                                                 verbose = F,
                                                 vars.to.regress = c("unique_name","nCount_RNA"),
                                                 variable.features.rv.th = 1, seed.use = 1,
                                                 return.only.var.genes = F, 
                                                 variable.features.n = NULL))
end_cells$healthy = RunPCA(end_cells$healthy, verbose = F)
end_cells$healthy = RunUMAP(end_cells$healthy, dims = 1:15, verbose = F)
DimPlot(end_cells$healthy, reduction = "umap", group.by = "unique_name")

end_cells$healthy = FindNeighbors(end_cells$healthy, reduction = "pca", dims = 1:15,
                                  force.recalc = T, graph.name = "end")
end_cells$healthy = FindClusters(end_cells$healthy, graph.name = "end", algorithm = 2, 
                                 resolution = seq(0.1,0.8,0.1), verbose = F)
DimPlot(end_cells$healthy, reduction = "umap", group.by = "end_res.0.2")
end_cells$healthy = SetIdent(end_cells$healthy, value = "end_res.0.2")
mk_end_h = FindAllMarkers(end_cells$healthy, assay = "SCT")

saveRDS(end_cells$healthy, file = "results/zonation_healthy/end_healthy_srat.RDS")

Save endothelial cells

saveRDS(end_cells, file="results/zonation_cond/end_cells.RDS")

Plot expression of zonation markers

gfresh = c("CLEC1B", "CLEC4G", "LYVE1", "CD14",  # central 
           "PECAM1", "AQP1", "VWF", "CD34") # portal

png("results/zonation_healthy/healthy_hepatocyte_zonation_markers_violin.png", 
    height = 1400, width = 1400)
VlnPlot(end_cells$healthy, features = gfresh, group.by = "names_clusters", 
        pt.size = 0, sort = "increasing")
dev.off()
png("results/zonation_healthy/healthy_hepatocyte_zonation_markers_umap.png", 
    height = 1400, width = 1400)
FeaturePlot(end_cells$healthy, features = gfresh, pt.size = 0.6)
dev.off()

Obtain Diffusion maps projection

set.seed(1)
dm = DiffusionMap(end_cells$healthy@reductions$pca@cell.embeddings[,1:4], 
                  rotate = T, n_eigs = 5)
dpt = DPT(dm, tips = c(2382, 1379, 424))
plot(dpt, col_by = "DPT424")
plot(dpt, col_by = "branch")

dm = DiffusionMap(end_cells$healthy@reductions$pca@cell.embeddings[dpt$DPT424<15,1:4], 
                  rotate = T, n_eigs = 5)
dpt = DPT(dm)
plot(dpt, col_by = "DPT363")

end_cells$healthy = end_cells$healthy[,names(dpt@dm$DC1)]

Check expression of some genes

set.seed(1)
dm = DiffusionMap(end_cells$healthy@reductions$pca@cell.embeddings[,1:4], 
                  rotate = T, n_eigs = 5)
dpt = DPT(dm, tips = c(2382, 1379, 424))
plot(dpt, col_by = "DPT424")

plot(dpt, col_by = "branch")


dm = DiffusionMap(end_cells$healthy@reductions$pca@cell.embeddings[dpt$DPT424<15,1:4], 
                  rotate = T, n_eigs = 5)
dpt = DPT(dm)
plot(dpt, col_by = "DPT363")


end_cells$healthy = end_cells$healthy[,names(dpt@dm$DC1)]

We’ll be using DPT363 as the pseudotime. This was the best approximation possible to the zonation gradient based on diffusion maps

pdf("results/zonation_healthy/end_distributions_DPT363.pdf", useDingbats = F, 
    height = 4, width = 9)
plt1 = ggplot(df_dc, aes(x = DPT, y = names_clusters, 
                  group = names_clusters, fill = names_clusters))+
  geom_density_ridges2(alpha = 0.4)+
  theme_bw()+
  theme(legend.position = "none")

plt2 = ggplot(df_dc, aes(x = DPT, y = unique_name, 
                  group = unique_name, fill = unique_name))+
  geom_density_ridges2(alpha = 0.4)+
  theme_bw()+
  theme(legend.position = "none")

cowplot::plot_grid(plt1, plt2, ncol = 2, align = "h")
dev.off()

Find genes varying along the pseudotime

# will only use variable genes
hvg = end_cells$healthy@assays$SCT@var.features

# Fit GAM for each gene using pseudotime as independent variable.
t = scales::rescale(rank(-df_dc$DPT), c(0.00001, 0.99999))
gene_fit_p = c()
gene_fit_vals = data.frame(row.names = 1:100)
for(i in 1:length(hvg)){
  g = hvg[i]
  z = end_cells$healthy@assays$SCT@data[g,]
  
  d = data.frame(z=z, t=t)
  tmp = suppressMessages(gam(z ~ ns(t, df = 3), data=d))
  
  # bins for model fitting
  bb = seq(min(t), max(t), length.out = 100)
  gene_fit_vals[,g] = suppressMessages(predict(tmp,
                                               newdata = data.frame(t = bb)))
  p = summary(tmp)$parametric.anova$`Pr(>F)`[1]
  gene_fit_p = c(gene_fit_p, p)
  names(gene_fit_p)[length(gene_fit_p)] = g
}

gene_fit_p = fdrtool::fdrtool(gene_fit_p, statistic="pvalue", plot=F, 
                              verbose=F, cutoff.method="pct0", pct0=0.9)$qval

Analysis of other conditions

First, clean the data from the other two conditions

# will only use variable genes
hvg = end_cells$healthy@assays$SCT@var.features

# Fit GAM for each gene using pseudotime as independent variable.
t = scales::rescale(rank(-df_dc$DPT), c(0.00001, 0.99999))
gene_fit_p = c()
gene_fit_vals = data.frame(row.names = 1:100)
for(i in 1:length(hvg)){
  g = hvg[i]
  z = end_cells$healthy@assays$SCT@data[g,]
  
  d = data.frame(z=z, t=t)
  tmp = suppressMessages(gam(z ~ ns(t, df = 3), data=d))
  
  # bins for model fitting
  bb = seq(min(t), max(t), length.out = 100)
  gene_fit_vals[,g] = suppressMessages(predict(tmp,
                                               newdata = data.frame(t = bb)))
  p = summary(tmp)$parametric.anova$`Pr(>F)`[1]
  gene_fit_p = c(gene_fit_p, p)
  names(gene_fit_p)[length(gene_fit_p)] = g
}

gene_fit_p = fdrtool::fdrtool(gene_fit_p, statistic="pvalue", plot=F, 
                              verbose=F, cutoff.method="pct0", pct0=0.9)$qval

Now use the top genes to learn the pseudozonation coordinates and project the embolised and regenerating data

end_cells$embolised = FindNeighbors(end_cells$embolised, reduction = "pca", dims = 1:20,
                                    force.recalc = T, graph.name = "endo")
Computing nearest neighbor graph
Computing SNN
end_cells$embolised = FindClusters(end_cells$embolised, graph.name = "endo", algorithm = 2, 
                                   resolution = 0.8, verbose = F)
DimPlot(end_cells$embolised, reduction = "umap", group.by = "endo_res.0.8")

end_cells$embolised = SetIdent(end_cells$embolised, value = "endo_res.0.8")
mk_emb = FindAllMarkers(end_cells$embolised, assay = "SCT")
Calculating cluster 0

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~02s          
  |++                                                | 3 % ~02s          
  |+++                                               | 4 % ~03s          
  |+++                                               | 6 % ~02s          
  |++++                                              | 7 % ~02s          
  |+++++                                             | 9 % ~02s          
  |++++++                                            | 10% ~02s          
  |++++++                                            | 12% ~02s          
  |+++++++                                           | 13% ~02s          
  |++++++++                                          | 14% ~02s          
  |++++++++                                          | 16% ~02s          
  |+++++++++                                         | 17% ~02s          
  |++++++++++                                        | 19% ~02s          
  |+++++++++++                                       | 20% ~02s          
  |+++++++++++                                       | 22% ~02s          
  |++++++++++++                                      | 23% ~02s          
  |+++++++++++++                                     | 25% ~02s          
  |++++++++++++++                                    | 26% ~02s          
  |++++++++++++++                                    | 28% ~02s          
  |+++++++++++++++                                   | 29% ~02s          
  |++++++++++++++++                                  | 30% ~02s          
  |++++++++++++++++                                  | 32% ~02s          
  |+++++++++++++++++                                 | 33% ~02s          
  |++++++++++++++++++                                | 35% ~02s          
  |+++++++++++++++++++                               | 36% ~02s          
  |+++++++++++++++++++                               | 38% ~02s          
  |++++++++++++++++++++                              | 39% ~02s          
  |+++++++++++++++++++++                             | 41% ~02s          
  |++++++++++++++++++++++                            | 42% ~02s          
  |++++++++++++++++++++++                            | 43% ~02s          
  |+++++++++++++++++++++++                           | 45% ~02s          
  |++++++++++++++++++++++++                          | 46% ~02s          
  |++++++++++++++++++++++++                          | 48% ~01s          
  |+++++++++++++++++++++++++                         | 49% ~01s          
  |++++++++++++++++++++++++++                        | 51% ~01s          
  |+++++++++++++++++++++++++++                       | 52% ~01s          
  |+++++++++++++++++++++++++++                       | 54% ~01s          
  |++++++++++++++++++++++++++++                      | 55% ~01s          
  |+++++++++++++++++++++++++++++                     | 57% ~01s          
  |+++++++++++++++++++++++++++++                     | 58% ~01s          
  |++++++++++++++++++++++++++++++                    | 59% ~01s          
  |+++++++++++++++++++++++++++++++                   | 61% ~01s          
  |++++++++++++++++++++++++++++++++                  | 62% ~01s          
  |++++++++++++++++++++++++++++++++                  | 64% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 65% ~01s          
  |++++++++++++++++++++++++++++++++++                | 67% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 68% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 70% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 72% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 74% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 78% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 84% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 88% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 94% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=03s  
Calculating cluster 1

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~04s          
  |++                                                | 3 % ~04s          
  |++                                                | 4 % ~04s          
  |+++                                               | 5 % ~04s          
  |++++                                              | 7 % ~04s          
  |++++                                              | 8 % ~04s          
  |+++++                                             | 9 % ~04s          
  |++++++                                            | 11% ~04s          
  |++++++                                            | 12% ~04s          
  |+++++++                                           | 13% ~04s          
  |++++++++                                          | 14% ~04s          
  |++++++++                                          | 16% ~03s          
  |+++++++++                                         | 17% ~03s          
  |++++++++++                                        | 18% ~03s          
  |++++++++++                                        | 20% ~03s          
  |+++++++++++                                       | 21% ~03s          
  |++++++++++++                                      | 22% ~03s          
  |++++++++++++                                      | 24% ~03s          
  |+++++++++++++                                     | 25% ~03s          
  |++++++++++++++                                    | 26% ~03s          
  |++++++++++++++                                    | 28% ~03s          
  |+++++++++++++++                                   | 29% ~03s          
  |++++++++++++++++                                  | 30% ~03s          
  |++++++++++++++++                                  | 32% ~03s          
  |+++++++++++++++++                                 | 33% ~03s          
  |++++++++++++++++++                                | 34% ~03s          
  |++++++++++++++++++                                | 36% ~03s          
  |+++++++++++++++++++                               | 37% ~03s          
  |++++++++++++++++++++                              | 38% ~03s          
  |++++++++++++++++++++                              | 39% ~03s          
  |+++++++++++++++++++++                             | 41% ~02s          
  |++++++++++++++++++++++                            | 42% ~02s          
  |++++++++++++++++++++++                            | 43% ~02s          
  |+++++++++++++++++++++++                           | 45% ~02s          
  |++++++++++++++++++++++++                          | 46% ~02s          
  |++++++++++++++++++++++++                          | 47% ~02s          
  |+++++++++++++++++++++++++                         | 49% ~02s          
  |+++++++++++++++++++++++++                         | 50% ~02s          
  |++++++++++++++++++++++++++                        | 51% ~02s          
  |+++++++++++++++++++++++++++                       | 53% ~02s          
  |+++++++++++++++++++++++++++                       | 54% ~02s          
  |++++++++++++++++++++++++++++                      | 55% ~02s          
  |+++++++++++++++++++++++++++++                     | 57% ~02s          
  |+++++++++++++++++++++++++++++                     | 58% ~02s          
  |++++++++++++++++++++++++++++++                    | 59% ~02s          
  |+++++++++++++++++++++++++++++++                   | 61% ~02s          
  |+++++++++++++++++++++++++++++++                   | 62% ~02s          
  |++++++++++++++++++++++++++++++++                  | 63% ~02s          
  |+++++++++++++++++++++++++++++++++                 | 64% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 66% ~01s          
  |++++++++++++++++++++++++++++++++++                | 67% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 68% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 70% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 72% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 74% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 76% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 78% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 80% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 82% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 84% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 88% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 92% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 96% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=04s  
Calculating cluster 2

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~02s          
  |++                                                | 2 % ~02s          
  |++                                                | 3 % ~02s          
  |+++                                               | 4 % ~02s          
  |+++                                               | 5 % ~02s          
  |++++                                              | 6 % ~02s          
  |++++                                              | 7 % ~02s          
  |+++++                                             | 9 % ~02s          
  |+++++                                             | 10% ~02s          
  |++++++                                            | 11% ~02s          
  |++++++                                            | 12% ~02s          
  |+++++++                                           | 13% ~02s          
  |+++++++                                           | 14% ~02s          
  |++++++++                                          | 15% ~02s          
  |++++++++                                          | 16% ~02s          
  |+++++++++                                         | 17% ~02s          
  |++++++++++                                        | 18% ~02s          
  |++++++++++                                        | 19% ~02s          
  |+++++++++++                                       | 20% ~02s          
  |+++++++++++                                       | 21% ~02s          
  |++++++++++++                                      | 22% ~02s          
  |++++++++++++                                      | 23% ~02s          
  |+++++++++++++                                     | 24% ~02s          
  |+++++++++++++                                     | 26% ~02s          
  |++++++++++++++                                    | 27% ~02s          
  |++++++++++++++                                    | 28% ~02s          
  |+++++++++++++++                                   | 29% ~02s          
  |+++++++++++++++                                   | 30% ~02s          
  |++++++++++++++++                                  | 31% ~02s          
  |++++++++++++++++                                  | 32% ~02s          
  |+++++++++++++++++                                 | 33% ~02s          
  |++++++++++++++++++                                | 34% ~02s          
  |++++++++++++++++++                                | 35% ~02s          
  |+++++++++++++++++++                               | 36% ~02s          
  |+++++++++++++++++++                               | 37% ~01s          
  |++++++++++++++++++++                              | 38% ~01s          
  |++++++++++++++++++++                              | 39% ~01s          
  |+++++++++++++++++++++                             | 40% ~01s          
  |+++++++++++++++++++++                             | 41% ~01s          
  |++++++++++++++++++++++                            | 43% ~01s          
  |++++++++++++++++++++++                            | 44% ~01s          
  |+++++++++++++++++++++++                           | 45% ~01s          
  |+++++++++++++++++++++++                           | 46% ~01s          
  |++++++++++++++++++++++++                          | 47% ~01s          
  |++++++++++++++++++++++++                          | 48% ~01s          
  |+++++++++++++++++++++++++                         | 49% ~01s          
  |+++++++++++++++++++++++++                         | 50% ~01s          
  |++++++++++++++++++++++++++                        | 51% ~01s          
  |+++++++++++++++++++++++++++                       | 52% ~01s          
  |+++++++++++++++++++++++++++                       | 53% ~01s          
  |++++++++++++++++++++++++++++                      | 54% ~01s          
  |++++++++++++++++++++++++++++                      | 55% ~01s          
  |+++++++++++++++++++++++++++++                     | 56% ~01s          
  |+++++++++++++++++++++++++++++                     | 57% ~01s          
  |++++++++++++++++++++++++++++++                    | 59% ~01s          
  |++++++++++++++++++++++++++++++                    | 60% ~01s          
  |+++++++++++++++++++++++++++++++                   | 61% ~01s          
  |+++++++++++++++++++++++++++++++                   | 62% ~01s          
  |++++++++++++++++++++++++++++++++                  | 63% ~01s          
  |++++++++++++++++++++++++++++++++                  | 64% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 65% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 66% ~01s          
  |++++++++++++++++++++++++++++++++++                | 67% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 68% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 69% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 70% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 72% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 73% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 74% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 76% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 78% ~00s          
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~00s          
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++         | 82% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 84% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 86% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 88% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 90% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 94% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=02s  
Calculating cluster 3

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~03s          
  |++                                                | 2 % ~03s          
  |++                                                | 3 % ~03s          
  |+++                                               | 4 % ~03s          
  |+++                                               | 5 % ~03s          
  |++++                                              | 7 % ~03s          
  |++++                                              | 8 % ~03s          
  |+++++                                             | 9 % ~02s          
  |+++++                                             | 10% ~02s          
  |++++++                                            | 11% ~02s          
  |++++++                                            | 12% ~02s          
  |+++++++                                           | 13% ~02s          
  |++++++++                                          | 14% ~02s          
  |++++++++                                          | 15% ~02s          
  |+++++++++                                         | 16% ~02s          
  |+++++++++                                         | 17% ~02s          
  |++++++++++                                        | 18% ~02s          
  |++++++++++                                        | 20% ~02s          
  |+++++++++++                                       | 21% ~02s          
  |+++++++++++                                       | 22% ~02s          
  |++++++++++++                                      | 23% ~02s          
  |++++++++++++                                      | 24% ~02s          
  |+++++++++++++                                     | 25% ~02s          
  |++++++++++++++                                    | 26% ~02s          
  |++++++++++++++                                    | 27% ~02s          
  |+++++++++++++++                                   | 28% ~02s          
  |+++++++++++++++                                   | 29% ~02s          
  |++++++++++++++++                                  | 30% ~02s          
  |++++++++++++++++                                  | 32% ~02s          
  |+++++++++++++++++                                 | 33% ~02s          
  |+++++++++++++++++                                 | 34% ~02s          
  |++++++++++++++++++                                | 35% ~02s          
  |++++++++++++++++++                                | 36% ~02s          
  |+++++++++++++++++++                               | 37% ~02s          
  |++++++++++++++++++++                              | 38% ~02s          
  |++++++++++++++++++++                              | 39% ~02s          
  |+++++++++++++++++++++                             | 40% ~02s          
  |+++++++++++++++++++++                             | 41% ~02s          
  |++++++++++++++++++++++                            | 42% ~01s          
  |++++++++++++++++++++++                            | 43% ~01s          
  |+++++++++++++++++++++++                           | 45% ~01s          
  |+++++++++++++++++++++++                           | 46% ~01s          
  |++++++++++++++++++++++++                          | 47% ~01s          
  |++++++++++++++++++++++++                          | 48% ~01s          
  |+++++++++++++++++++++++++                         | 49% ~01s          
  |+++++++++++++++++++++++++                         | 50% ~01s          
  |++++++++++++++++++++++++++                        | 51% ~01s          
  |+++++++++++++++++++++++++++                       | 52% ~01s          
  |+++++++++++++++++++++++++++                       | 53% ~01s          
  |++++++++++++++++++++++++++++                      | 54% ~01s          
  |++++++++++++++++++++++++++++                      | 55% ~01s          
  |+++++++++++++++++++++++++++++                     | 57% ~01s          
  |+++++++++++++++++++++++++++++                     | 58% ~01s          
  |++++++++++++++++++++++++++++++                    | 59% ~01s          
  |++++++++++++++++++++++++++++++                    | 60% ~01s          
  |+++++++++++++++++++++++++++++++                   | 61% ~01s          
  |+++++++++++++++++++++++++++++++                   | 62% ~01s          
  |++++++++++++++++++++++++++++++++                  | 63% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 64% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 65% ~01s          
  |++++++++++++++++++++++++++++++++++                | 66% ~01s          
  |++++++++++++++++++++++++++++++++++                | 67% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 68% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 70% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 72% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 73% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 74% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 76% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 78% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 80% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 82% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++        | 84% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 88% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 90% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 92% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=03s  
Calculating cluster 4

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~04s          
  |++                                                | 2 % ~03s          
  |++                                                | 3 % ~03s          
  |+++                                               | 4 % ~03s          
  |+++                                               | 5 % ~03s          
  |++++                                              | 7 % ~03s          
  |++++                                              | 8 % ~03s          
  |+++++                                             | 9 % ~03s          
  |+++++                                             | 10% ~03s          
  |++++++                                            | 11% ~03s          
  |++++++                                            | 12% ~03s          
  |+++++++                                           | 13% ~03s          
  |++++++++                                          | 14% ~03s          
  |++++++++                                          | 15% ~03s          
  |+++++++++                                         | 16% ~03s          
  |+++++++++                                         | 17% ~03s          
  |++++++++++                                        | 18% ~03s          
  |++++++++++                                        | 20% ~03s          
  |+++++++++++                                       | 21% ~03s          
  |+++++++++++                                       | 22% ~03s          
  |++++++++++++                                      | 23% ~03s          
  |++++++++++++                                      | 24% ~03s          
  |+++++++++++++                                     | 25% ~02s          
  |++++++++++++++                                    | 26% ~02s          
  |++++++++++++++                                    | 27% ~02s          
  |+++++++++++++++                                   | 28% ~02s          
  |+++++++++++++++                                   | 29% ~02s          
  |++++++++++++++++                                  | 30% ~02s          
  |++++++++++++++++                                  | 32% ~02s          
  |+++++++++++++++++                                 | 33% ~02s          
  |+++++++++++++++++                                 | 34% ~02s          
  |++++++++++++++++++                                | 35% ~02s          
  |++++++++++++++++++                                | 36% ~02s          
  |+++++++++++++++++++                               | 37% ~02s          
  |++++++++++++++++++++                              | 38% ~02s          
  |++++++++++++++++++++                              | 39% ~02s          
  |+++++++++++++++++++++                             | 40% ~02s          
  |+++++++++++++++++++++                             | 41% ~02s          
  |++++++++++++++++++++++                            | 42% ~02s          
  |++++++++++++++++++++++                            | 43% ~02s          
  |+++++++++++++++++++++++                           | 45% ~02s          
  |+++++++++++++++++++++++                           | 46% ~02s          
  |++++++++++++++++++++++++                          | 47% ~02s          
  |++++++++++++++++++++++++                          | 48% ~02s          
  |+++++++++++++++++++++++++                         | 49% ~02s          
  |+++++++++++++++++++++++++                         | 50% ~02s          
  |++++++++++++++++++++++++++                        | 51% ~02s          
  |+++++++++++++++++++++++++++                       | 52% ~02s          
  |+++++++++++++++++++++++++++                       | 53% ~02s          
  |++++++++++++++++++++++++++++                      | 54% ~02s          
  |++++++++++++++++++++++++++++                      | 55% ~01s          
  |+++++++++++++++++++++++++++++                     | 57% ~01s          
  |+++++++++++++++++++++++++++++                     | 58% ~01s          
  |++++++++++++++++++++++++++++++                    | 59% ~01s          
  |++++++++++++++++++++++++++++++                    | 60% ~01s          
  |+++++++++++++++++++++++++++++++                   | 61% ~01s          
  |+++++++++++++++++++++++++++++++                   | 62% ~01s          
  |++++++++++++++++++++++++++++++++                  | 63% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 64% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 65% ~01s          
  |++++++++++++++++++++++++++++++++++                | 66% ~01s          
  |++++++++++++++++++++++++++++++++++                | 67% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 68% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 70% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 72% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 73% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 74% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 76% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 78% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 80% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 82% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 84% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 88% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 90% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 92% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=03s  
Calculating cluster 5

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~03s          
  |++                                                | 2 % ~03s          
  |++                                                | 3 % ~03s          
  |+++                                               | 4 % ~03s          
  |+++                                               | 5 % ~03s          
  |++++                                              | 6 % ~03s          
  |++++                                              | 7 % ~03s          
  |+++++                                             | 8 % ~02s          
  |+++++                                             | 9 % ~02s          
  |++++++                                            | 10% ~02s          
  |++++++                                            | 11% ~02s          
  |+++++++                                           | 12% ~02s          
  |+++++++                                           | 13% ~02s          
  |++++++++                                          | 14% ~02s          
  |++++++++                                          | 15% ~02s          
  |+++++++++                                         | 16% ~02s          
  |+++++++++                                         | 18% ~02s          
  |++++++++++                                        | 19% ~02s          
  |++++++++++                                        | 20% ~02s          
  |+++++++++++                                       | 21% ~02s          
  |+++++++++++                                       | 22% ~02s          
  |++++++++++++                                      | 23% ~02s          
  |++++++++++++                                      | 24% ~02s          
  |+++++++++++++                                     | 25% ~02s          
  |+++++++++++++                                     | 26% ~02s          
  |++++++++++++++                                    | 27% ~02s          
  |++++++++++++++                                    | 28% ~02s          
  |+++++++++++++++                                   | 29% ~02s          
  |+++++++++++++++                                   | 30% ~02s          
  |++++++++++++++++                                  | 31% ~02s          
  |++++++++++++++++                                  | 32% ~02s          
  |+++++++++++++++++                                 | 33% ~02s          
  |++++++++++++++++++                                | 34% ~02s          
  |++++++++++++++++++                                | 35% ~02s          
  |+++++++++++++++++++                               | 36% ~02s          
  |+++++++++++++++++++                               | 37% ~02s          
  |++++++++++++++++++++                              | 38% ~02s          
  |++++++++++++++++++++                              | 39% ~02s          
  |+++++++++++++++++++++                             | 40% ~02s          
  |+++++++++++++++++++++                             | 41% ~02s          
  |++++++++++++++++++++++                            | 42% ~02s          
  |++++++++++++++++++++++                            | 43% ~01s          
  |+++++++++++++++++++++++                           | 44% ~01s          
  |+++++++++++++++++++++++                           | 45% ~01s          
  |++++++++++++++++++++++++                          | 46% ~01s          
  |++++++++++++++++++++++++                          | 47% ~01s          
  |+++++++++++++++++++++++++                         | 48% ~01s          
  |+++++++++++++++++++++++++                         | 49% ~01s          
  |++++++++++++++++++++++++++                        | 51% ~01s          
  |++++++++++++++++++++++++++                        | 52% ~01s          
  |+++++++++++++++++++++++++++                       | 53% ~01s          
  |+++++++++++++++++++++++++++                       | 54% ~01s          
  |++++++++++++++++++++++++++++                      | 55% ~01s          
  |++++++++++++++++++++++++++++                      | 56% ~01s          
  |+++++++++++++++++++++++++++++                     | 57% ~01s          
  |+++++++++++++++++++++++++++++                     | 58% ~01s          
  |++++++++++++++++++++++++++++++                    | 59% ~01s          
  |++++++++++++++++++++++++++++++                    | 60% ~01s          
  |+++++++++++++++++++++++++++++++                   | 61% ~01s          
  |+++++++++++++++++++++++++++++++                   | 62% ~01s          
  |++++++++++++++++++++++++++++++++                  | 63% ~01s          
  |++++++++++++++++++++++++++++++++                  | 64% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 65% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 66% ~01s          
  |++++++++++++++++++++++++++++++++++                | 67% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 68% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 69% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 70% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 72% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 73% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 74% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 76% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 78% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 80% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++        | 82% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++        | 84% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 94% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=03s  
Calculating cluster 6

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~03s          
  |++                                                | 3 % ~03s          
  |++                                                | 4 % ~03s          
  |+++                                               | 5 % ~03s          
  |++++                                              | 7 % ~03s          
  |++++                                              | 8 % ~02s          
  |+++++                                             | 9 % ~02s          
  |++++++                                            | 11% ~02s          
  |++++++                                            | 12% ~02s          
  |+++++++                                           | 13% ~02s          
  |++++++++                                          | 15% ~02s          
  |++++++++                                          | 16% ~02s          
  |+++++++++                                         | 17% ~02s          
  |++++++++++                                        | 19% ~02s          
  |++++++++++                                        | 20% ~02s          
  |+++++++++++                                       | 21% ~02s          
  |++++++++++++                                      | 23% ~02s          
  |++++++++++++                                      | 24% ~02s          
  |+++++++++++++                                     | 25% ~02s          
  |++++++++++++++                                    | 27% ~02s          
  |++++++++++++++                                    | 28% ~02s          
  |+++++++++++++++                                   | 29% ~02s          
  |++++++++++++++++                                  | 31% ~02s          
  |++++++++++++++++                                  | 32% ~02s          
  |+++++++++++++++++                                 | 33% ~02s          
  |++++++++++++++++++                                | 35% ~02s          
  |++++++++++++++++++                                | 36% ~02s          
  |+++++++++++++++++++                               | 37% ~02s          
  |++++++++++++++++++++                              | 39% ~02s          
  |++++++++++++++++++++                              | 40% ~02s          
  |+++++++++++++++++++++                             | 41% ~02s          
  |++++++++++++++++++++++                            | 43% ~02s          
  |++++++++++++++++++++++                            | 44% ~02s          
  |+++++++++++++++++++++++                           | 45% ~02s          
  |++++++++++++++++++++++++                          | 47% ~01s          
  |++++++++++++++++++++++++                          | 48% ~01s          
  |+++++++++++++++++++++++++                         | 49% ~01s          
  |++++++++++++++++++++++++++                        | 51% ~01s          
  |++++++++++++++++++++++++++                        | 52% ~01s          
  |+++++++++++++++++++++++++++                       | 53% ~01s          
  |++++++++++++++++++++++++++++                      | 55% ~01s          
  |++++++++++++++++++++++++++++                      | 56% ~01s          
  |+++++++++++++++++++++++++++++                     | 57% ~01s          
  |++++++++++++++++++++++++++++++                    | 59% ~01s          
  |++++++++++++++++++++++++++++++                    | 60% ~01s          
  |+++++++++++++++++++++++++++++++                   | 61% ~01s          
  |++++++++++++++++++++++++++++++++                  | 63% ~01s          
  |++++++++++++++++++++++++++++++++                  | 64% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 65% ~01s          
  |++++++++++++++++++++++++++++++++++                | 67% ~01s          
  |++++++++++++++++++++++++++++++++++                | 68% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 69% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 72% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 73% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 76% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++        | 84% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=03s  
Calculating cluster 7

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~06s          
  |++                                                | 2 % ~06s          
  |++                                                | 3 % ~06s          
  |+++                                               | 4 % ~06s          
  |+++                                               | 5 % ~06s          
  |++++                                              | 6 % ~05s          
  |++++                                              | 7 % ~05s          
  |+++++                                             | 8 % ~05s          
  |+++++                                             | 9 % ~05s          
  |++++++                                            | 10% ~05s          
  |++++++                                            | 11% ~05s          
  |+++++++                                           | 12% ~05s          
  |+++++++                                           | 13% ~05s          
  |++++++++                                          | 14% ~05s          
  |++++++++                                          | 15% ~05s          
  |+++++++++                                         | 16% ~05s          
  |+++++++++                                         | 18% ~05s          
  |++++++++++                                        | 19% ~05s          
  |++++++++++                                        | 20% ~05s          
  |+++++++++++                                       | 21% ~05s          
  |+++++++++++                                       | 22% ~05s          
  |++++++++++++                                      | 23% ~05s          
  |++++++++++++                                      | 24% ~05s          
  |+++++++++++++                                     | 25% ~04s          
  |+++++++++++++                                     | 26% ~04s          
  |++++++++++++++                                    | 27% ~04s          
  |++++++++++++++                                    | 28% ~04s          
  |+++++++++++++++                                   | 29% ~04s          
  |+++++++++++++++                                   | 30% ~04s          
  |++++++++++++++++                                  | 31% ~04s          
  |++++++++++++++++                                  | 32% ~04s          
  |+++++++++++++++++                                 | 33% ~04s          
  |++++++++++++++++++                                | 34% ~04s          
  |++++++++++++++++++                                | 35% ~04s          
  |+++++++++++++++++++                               | 36% ~04s          
  |+++++++++++++++++++                               | 37% ~04s          
  |++++++++++++++++++++                              | 38% ~04s          
  |++++++++++++++++++++                              | 39% ~04s          
  |+++++++++++++++++++++                             | 40% ~04s          
  |+++++++++++++++++++++                             | 41% ~03s          
  |++++++++++++++++++++++                            | 42% ~03s          
  |++++++++++++++++++++++                            | 43% ~03s          
  |+++++++++++++++++++++++                           | 44% ~03s          
  |+++++++++++++++++++++++                           | 45% ~03s          
  |++++++++++++++++++++++++                          | 46% ~03s          
  |++++++++++++++++++++++++                          | 47% ~03s          
  |+++++++++++++++++++++++++                         | 48% ~03s          
  |+++++++++++++++++++++++++                         | 49% ~03s          
  |++++++++++++++++++++++++++                        | 51% ~03s          
  |++++++++++++++++++++++++++                        | 52% ~03s          
  |+++++++++++++++++++++++++++                       | 53% ~03s          
  |+++++++++++++++++++++++++++                       | 54% ~03s          
  |++++++++++++++++++++++++++++                      | 55% ~03s          
  |++++++++++++++++++++++++++++                      | 56% ~03s          
  |+++++++++++++++++++++++++++++                     | 57% ~03s          
  |+++++++++++++++++++++++++++++                     | 58% ~03s          
  |++++++++++++++++++++++++++++++                    | 59% ~02s          
  |++++++++++++++++++++++++++++++                    | 60% ~02s          
  |+++++++++++++++++++++++++++++++                   | 61% ~02s          
  |+++++++++++++++++++++++++++++++                   | 62% ~02s          
  |++++++++++++++++++++++++++++++++                  | 63% ~02s          
  |++++++++++++++++++++++++++++++++                  | 64% ~02s          
  |+++++++++++++++++++++++++++++++++                 | 65% ~02s          
  |+++++++++++++++++++++++++++++++++                 | 66% ~02s          
  |++++++++++++++++++++++++++++++++++                | 67% ~02s          
  |+++++++++++++++++++++++++++++++++++               | 68% ~02s          
  |+++++++++++++++++++++++++++++++++++               | 69% ~02s          
  |++++++++++++++++++++++++++++++++++++              | 70% ~02s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~02s          
  |+++++++++++++++++++++++++++++++++++++             | 72% ~02s          
  |+++++++++++++++++++++++++++++++++++++             | 73% ~02s          
  |++++++++++++++++++++++++++++++++++++++            | 74% ~02s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 76% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 78% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 80% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 82% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 84% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 94% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=06s  
Calculating cluster 8

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~04s          
  |++                                                | 2 % ~04s          
  |++                                                | 3 % ~04s          
  |+++                                               | 4 % ~04s          
  |+++                                               | 5 % ~04s          
  |++++                                              | 6 % ~03s          
  |++++                                              | 7 % ~03s          
  |+++++                                             | 8 % ~03s          
  |+++++                                             | 9 % ~03s          
  |++++++                                            | 10% ~03s          
  |++++++                                            | 11% ~03s          
  |+++++++                                           | 12% ~03s          
  |+++++++                                           | 13% ~03s          
  |++++++++                                          | 14% ~03s          
  |++++++++                                          | 15% ~03s          
  |+++++++++                                         | 16% ~03s          
  |+++++++++                                         | 17% ~03s          
  |++++++++++                                        | 18% ~03s          
  |++++++++++                                        | 19% ~03s          
  |+++++++++++                                       | 20% ~03s          
  |+++++++++++                                       | 21% ~03s          
  |++++++++++++                                      | 22% ~03s          
  |++++++++++++                                      | 23% ~03s          
  |+++++++++++++                                     | 24% ~03s          
  |+++++++++++++                                     | 26% ~03s          
  |++++++++++++++                                    | 27% ~03s          
  |++++++++++++++                                    | 28% ~03s          
  |+++++++++++++++                                   | 29% ~03s          
  |+++++++++++++++                                   | 30% ~03s          
  |++++++++++++++++                                  | 31% ~03s          
  |++++++++++++++++                                  | 32% ~03s          
  |+++++++++++++++++                                 | 33% ~03s          
  |+++++++++++++++++                                 | 34% ~02s          
  |++++++++++++++++++                                | 35% ~02s          
  |++++++++++++++++++                                | 36% ~02s          
  |+++++++++++++++++++                               | 37% ~02s          
  |+++++++++++++++++++                               | 38% ~02s          
  |++++++++++++++++++++                              | 39% ~02s          
  |++++++++++++++++++++                              | 40% ~02s          
  |+++++++++++++++++++++                             | 41% ~02s          
  |+++++++++++++++++++++                             | 42% ~02s          
  |++++++++++++++++++++++                            | 43% ~02s          
  |++++++++++++++++++++++                            | 44% ~02s          
  |+++++++++++++++++++++++                           | 45% ~02s          
  |+++++++++++++++++++++++                           | 46% ~02s          
  |++++++++++++++++++++++++                          | 47% ~02s          
  |++++++++++++++++++++++++                          | 48% ~02s          
  |+++++++++++++++++++++++++                         | 49% ~02s          
  |+++++++++++++++++++++++++                         | 50% ~02s          
  |++++++++++++++++++++++++++                        | 51% ~02s          
  |+++++++++++++++++++++++++++                       | 52% ~02s          
  |+++++++++++++++++++++++++++                       | 53% ~02s          
  |++++++++++++++++++++++++++++                      | 54% ~02s          
  |++++++++++++++++++++++++++++                      | 55% ~02s          
  |+++++++++++++++++++++++++++++                     | 56% ~02s          
  |+++++++++++++++++++++++++++++                     | 57% ~02s          
  |++++++++++++++++++++++++++++++                    | 58% ~02s          
  |++++++++++++++++++++++++++++++                    | 59% ~02s          
  |+++++++++++++++++++++++++++++++                   | 60% ~01s          
  |+++++++++++++++++++++++++++++++                   | 61% ~01s          
  |++++++++++++++++++++++++++++++++                  | 62% ~01s          
  |++++++++++++++++++++++++++++++++                  | 63% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 64% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 65% ~01s          
  |++++++++++++++++++++++++++++++++++                | 66% ~01s          
  |++++++++++++++++++++++++++++++++++                | 67% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 68% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 69% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 70% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 72% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 73% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 74% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 76% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 78% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 82% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 84% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 94% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=04s  
Calculating cluster 9

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~03s          
  |++                                                | 2 % ~03s          
  |++                                                | 3 % ~03s          
  |+++                                               | 4 % ~03s          
  |+++                                               | 6 % ~03s          
  |++++                                              | 7 % ~03s          
  |++++                                              | 8 % ~03s          
  |+++++                                             | 9 % ~03s          
  |++++++                                            | 10% ~03s          
  |++++++                                            | 11% ~03s          
  |+++++++                                           | 12% ~03s          
  |+++++++                                           | 13% ~03s          
  |++++++++                                          | 15% ~03s          
  |++++++++                                          | 16% ~03s          
  |+++++++++                                         | 17% ~03s          
  |+++++++++                                         | 18% ~03s          
  |++++++++++                                        | 19% ~03s          
  |+++++++++++                                       | 20% ~03s          
  |+++++++++++                                       | 21% ~02s          
  |++++++++++++                                      | 22% ~02s          
  |++++++++++++                                      | 24% ~02s          
  |+++++++++++++                                     | 25% ~02s          
  |+++++++++++++                                     | 26% ~02s          
  |++++++++++++++                                    | 27% ~02s          
  |+++++++++++++++                                   | 28% ~02s          
  |+++++++++++++++                                   | 29% ~02s          
  |++++++++++++++++                                  | 30% ~02s          
  |++++++++++++++++                                  | 31% ~02s          
  |+++++++++++++++++                                 | 33% ~02s          
  |+++++++++++++++++                                 | 34% ~02s          
  |++++++++++++++++++                                | 35% ~02s          
  |++++++++++++++++++                                | 36% ~02s          
  |+++++++++++++++++++                               | 37% ~02s          
  |++++++++++++++++++++                              | 38% ~02s          
  |++++++++++++++++++++                              | 39% ~02s          
  |+++++++++++++++++++++                             | 40% ~02s          
  |+++++++++++++++++++++                             | 42% ~02s          
  |++++++++++++++++++++++                            | 43% ~02s          
  |++++++++++++++++++++++                            | 44% ~02s          
  |+++++++++++++++++++++++                           | 45% ~02s          
  |++++++++++++++++++++++++                          | 46% ~02s          
  |++++++++++++++++++++++++                          | 47% ~02s          
  |+++++++++++++++++++++++++                         | 48% ~02s          
  |+++++++++++++++++++++++++                         | 49% ~02s          
  |++++++++++++++++++++++++++                        | 51% ~02s          
  |++++++++++++++++++++++++++                        | 52% ~02s          
  |+++++++++++++++++++++++++++                       | 53% ~02s          
  |+++++++++++++++++++++++++++                       | 54% ~01s          
  |++++++++++++++++++++++++++++                      | 55% ~01s          
  |+++++++++++++++++++++++++++++                     | 56% ~01s          
  |+++++++++++++++++++++++++++++                     | 57% ~01s          
  |++++++++++++++++++++++++++++++                    | 58% ~01s          
  |++++++++++++++++++++++++++++++                    | 60% ~01s          
  |+++++++++++++++++++++++++++++++                   | 61% ~01s          
  |+++++++++++++++++++++++++++++++                   | 62% ~01s          
  |++++++++++++++++++++++++++++++++                  | 63% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 64% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 65% ~01s          
  |++++++++++++++++++++++++++++++++++                | 66% ~01s          
  |++++++++++++++++++++++++++++++++++                | 67% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 69% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 70% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 72% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 73% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 74% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 76% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 78% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 82% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 84% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 92% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 94% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=03s  
Calculating cluster 10

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~03s          
  |++                                                | 2 % ~03s          
  |++                                                | 4 % ~03s          
  |+++                                               | 5 % ~03s          
  |++++                                              | 6 % ~03s          
  |++++                                              | 8 % ~03s          
  |+++++                                             | 9 % ~03s          
  |+++++                                             | 10% ~02s          
  |++++++                                            | 11% ~02s          
  |+++++++                                           | 12% ~02s          
  |+++++++                                           | 14% ~02s          
  |++++++++                                          | 15% ~02s          
  |+++++++++                                         | 16% ~02s          
  |+++++++++                                         | 18% ~02s          
  |++++++++++                                        | 19% ~02s          
  |++++++++++                                        | 20% ~02s          
  |+++++++++++                                       | 21% ~02s          
  |++++++++++++                                      | 22% ~02s          
  |++++++++++++                                      | 24% ~02s          
  |+++++++++++++                                     | 25% ~02s          
  |++++++++++++++                                    | 26% ~02s          
  |++++++++++++++                                    | 28% ~02s          
  |+++++++++++++++                                   | 29% ~02s          
  |+++++++++++++++                                   | 30% ~02s          
  |++++++++++++++++                                  | 31% ~02s          
  |+++++++++++++++++                                 | 32% ~02s          
  |+++++++++++++++++                                 | 34% ~02s          
  |++++++++++++++++++                                | 35% ~02s          
  |+++++++++++++++++++                               | 36% ~02s          
  |+++++++++++++++++++                               | 38% ~02s          
  |++++++++++++++++++++                              | 39% ~02s          
  |++++++++++++++++++++                              | 40% ~02s          
  |+++++++++++++++++++++                             | 41% ~02s          
  |++++++++++++++++++++++                            | 42% ~02s          
  |++++++++++++++++++++++                            | 44% ~02s          
  |+++++++++++++++++++++++                           | 45% ~01s          
  |++++++++++++++++++++++++                          | 46% ~01s          
  |++++++++++++++++++++++++                          | 48% ~01s          
  |+++++++++++++++++++++++++                         | 49% ~01s          
  |+++++++++++++++++++++++++                         | 50% ~01s          
  |++++++++++++++++++++++++++                        | 51% ~01s          
  |+++++++++++++++++++++++++++                       | 52% ~01s          
  |+++++++++++++++++++++++++++                       | 54% ~01s          
  |++++++++++++++++++++++++++++                      | 55% ~01s          
  |+++++++++++++++++++++++++++++                     | 56% ~01s          
  |+++++++++++++++++++++++++++++                     | 58% ~01s          
  |++++++++++++++++++++++++++++++                    | 59% ~01s          
  |++++++++++++++++++++++++++++++                    | 60% ~01s          
  |+++++++++++++++++++++++++++++++                   | 61% ~01s          
  |++++++++++++++++++++++++++++++++                  | 62% ~01s          
  |++++++++++++++++++++++++++++++++                  | 64% ~01s          
  |+++++++++++++++++++++++++++++++++                 | 65% ~01s          
  |++++++++++++++++++++++++++++++++++                | 66% ~01s          
  |++++++++++++++++++++++++++++++++++                | 68% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 69% ~01s          
  |+++++++++++++++++++++++++++++++++++               | 70% ~01s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 72% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 74% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 76% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 78% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 82% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++        | 84% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 86% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 92% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 94% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 96% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=03s  
Calculating cluster 11

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~07s          
  |++                                                | 2 % ~07s          
  |++                                                | 3 % ~06s          
  |+++                                               | 4 % ~06s          
  |+++                                               | 5 % ~06s          
  |++++                                              | 6 % ~06s          
  |++++                                              | 7 % ~06s          
  |+++++                                             | 8 % ~06s          
  |+++++                                             | 9 % ~05s          
  |++++++                                            | 10% ~05s          
  |++++++                                            | 11% ~05s          
  |+++++++                                           | 12% ~05s          
  |+++++++                                           | 14% ~05s          
  |++++++++                                          | 15% ~05s          
  |++++++++                                          | 16% ~05s          
  |+++++++++                                         | 17% ~05s          
  |+++++++++                                         | 18% ~05s          
  |++++++++++                                        | 19% ~05s          
  |++++++++++                                        | 20% ~05s          
  |+++++++++++                                       | 21% ~05s          
  |+++++++++++                                       | 22% ~05s          
  |++++++++++++                                      | 23% ~05s          
  |++++++++++++                                      | 24% ~05s          
  |+++++++++++++                                     | 25% ~05s          
  |++++++++++++++                                    | 26% ~05s          
  |++++++++++++++                                    | 27% ~05s          
  |+++++++++++++++                                   | 28% ~05s          
  |+++++++++++++++                                   | 29% ~05s          
  |++++++++++++++++                                  | 30% ~04s          
  |++++++++++++++++                                  | 31% ~04s          
  |+++++++++++++++++                                 | 32% ~04s          
  |+++++++++++++++++                                 | 33% ~04s          
  |++++++++++++++++++                                | 34% ~04s          
  |++++++++++++++++++                                | 35% ~04s          
  |+++++++++++++++++++                               | 36% ~04s          
  |+++++++++++++++++++                               | 38% ~04s          
  |++++++++++++++++++++                              | 39% ~04s          
  |++++++++++++++++++++                              | 40% ~04s          
  |+++++++++++++++++++++                             | 41% ~04s          
  |+++++++++++++++++++++                             | 42% ~04s          
  |++++++++++++++++++++++                            | 43% ~03s          
  |++++++++++++++++++++++                            | 44% ~03s          
  |+++++++++++++++++++++++                           | 45% ~03s          
  |+++++++++++++++++++++++                           | 46% ~03s          
  |++++++++++++++++++++++++                          | 47% ~03s          
  |++++++++++++++++++++++++                          | 48% ~03s          
  |+++++++++++++++++++++++++                         | 49% ~03s          
  |+++++++++++++++++++++++++                         | 50% ~03s          
  |++++++++++++++++++++++++++                        | 51% ~03s          
  |+++++++++++++++++++++++++++                       | 52% ~03s          
  |+++++++++++++++++++++++++++                       | 53% ~03s          
  |++++++++++++++++++++++++++++                      | 54% ~03s          
  |++++++++++++++++++++++++++++                      | 55% ~03s          
  |+++++++++++++++++++++++++++++                     | 56% ~03s          
  |+++++++++++++++++++++++++++++                     | 57% ~03s          
  |++++++++++++++++++++++++++++++                    | 58% ~03s          
  |++++++++++++++++++++++++++++++                    | 59% ~02s          
  |+++++++++++++++++++++++++++++++                   | 60% ~02s          
  |+++++++++++++++++++++++++++++++                   | 61% ~02s          
  |++++++++++++++++++++++++++++++++                  | 62% ~02s          
  |++++++++++++++++++++++++++++++++                  | 64% ~02s          
  |+++++++++++++++++++++++++++++++++                 | 65% ~02s          
  |+++++++++++++++++++++++++++++++++                 | 66% ~02s          
  |++++++++++++++++++++++++++++++++++                | 67% ~02s          
  |++++++++++++++++++++++++++++++++++                | 68% ~02s          
  |+++++++++++++++++++++++++++++++++++               | 69% ~02s          
  |+++++++++++++++++++++++++++++++++++               | 70% ~02s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~02s          
  |++++++++++++++++++++++++++++++++++++              | 72% ~02s          
  |+++++++++++++++++++++++++++++++++++++             | 73% ~02s          
  |+++++++++++++++++++++++++++++++++++++             | 74% ~02s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~02s          
  |+++++++++++++++++++++++++++++++++++++++           | 76% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 78% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 80% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 82% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 84% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 86% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 94% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=06s  
Calculating cluster 12

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~07s          
  |++                                                | 2 % ~07s          
  |++                                                | 3 % ~07s          
  |+++                                               | 4 % ~07s          
  |+++                                               | 5 % ~07s          
  |++++                                              | 6 % ~07s          
  |++++                                              | 7 % ~07s          
  |+++++                                             | 8 % ~07s          
  |+++++                                             | 9 % ~07s          
  |++++++                                            | 10% ~07s          
  |++++++                                            | 11% ~07s          
  |+++++++                                           | 12% ~06s          
  |+++++++                                           | 13% ~06s          
  |++++++++                                          | 14% ~06s          
  |++++++++                                          | 15% ~06s          
  |+++++++++                                         | 16% ~06s          
  |+++++++++                                         | 17% ~06s          
  |++++++++++                                        | 18% ~06s          
  |++++++++++                                        | 19% ~06s          
  |+++++++++++                                       | 20% ~06s          
  |+++++++++++                                       | 21% ~06s          
  |++++++++++++                                      | 22% ~06s          
  |++++++++++++                                      | 23% ~05s          
  |+++++++++++++                                     | 24% ~05s          
  |+++++++++++++                                     | 25% ~05s          
  |++++++++++++++                                    | 26% ~05s          
  |++++++++++++++                                    | 27% ~05s          
  |+++++++++++++++                                   | 28% ~05s          
  |+++++++++++++++                                   | 29% ~05s          
  |++++++++++++++++                                  | 30% ~05s          
  |++++++++++++++++                                  | 31% ~05s          
  |+++++++++++++++++                                 | 32% ~05s          
  |+++++++++++++++++                                 | 33% ~05s          
  |++++++++++++++++++                                | 34% ~05s          
  |++++++++++++++++++                                | 35% ~05s          
  |+++++++++++++++++++                               | 36% ~05s          
  |+++++++++++++++++++                               | 37% ~04s          
  |++++++++++++++++++++                              | 38% ~04s          
  |++++++++++++++++++++                              | 39% ~04s          
  |+++++++++++++++++++++                             | 40% ~04s          
  |+++++++++++++++++++++                             | 41% ~04s          
  |++++++++++++++++++++++                            | 42% ~04s          
  |++++++++++++++++++++++                            | 43% ~04s          
  |+++++++++++++++++++++++                           | 44% ~04s          
  |+++++++++++++++++++++++                           | 45% ~04s          
  |++++++++++++++++++++++++                          | 46% ~04s          
  |++++++++++++++++++++++++                          | 47% ~04s          
  |+++++++++++++++++++++++++                         | 48% ~04s          
  |+++++++++++++++++++++++++                         | 49% ~04s          
  |++++++++++++++++++++++++++                        | 51% ~04s          
  |++++++++++++++++++++++++++                        | 52% ~03s          
  |+++++++++++++++++++++++++++                       | 53% ~03s          
  |+++++++++++++++++++++++++++                       | 54% ~03s          
  |++++++++++++++++++++++++++++                      | 55% ~03s          
  |++++++++++++++++++++++++++++                      | 56% ~03s          
  |+++++++++++++++++++++++++++++                     | 57% ~03s          
  |+++++++++++++++++++++++++++++                     | 58% ~03s          
  |++++++++++++++++++++++++++++++                    | 59% ~03s          
  |++++++++++++++++++++++++++++++                    | 60% ~03s          
  |+++++++++++++++++++++++++++++++                   | 61% ~03s          
  |+++++++++++++++++++++++++++++++                   | 62% ~03s          
  |++++++++++++++++++++++++++++++++                  | 63% ~03s          
  |++++++++++++++++++++++++++++++++                  | 64% ~03s          
  |+++++++++++++++++++++++++++++++++                 | 65% ~03s          
  |+++++++++++++++++++++++++++++++++                 | 66% ~02s          
  |++++++++++++++++++++++++++++++++++                | 67% ~02s          
  |++++++++++++++++++++++++++++++++++                | 68% ~02s          
  |+++++++++++++++++++++++++++++++++++               | 69% ~02s          
  |+++++++++++++++++++++++++++++++++++               | 70% ~02s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~02s          
  |++++++++++++++++++++++++++++++++++++              | 72% ~02s          
  |+++++++++++++++++++++++++++++++++++++             | 73% ~02s          
  |+++++++++++++++++++++++++++++++++++++             | 74% ~02s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~02s          
  |++++++++++++++++++++++++++++++++++++++            | 76% ~02s          
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~02s          
  |+++++++++++++++++++++++++++++++++++++++           | 78% ~02s          
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~02s          
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 82% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 84% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 94% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=07s  
outcells = (end_cells$embolised@reductions$umap@cell.embeddings[,2]>2.3 & 
              end_cells$embolised@reductions$umap@cell.embeddings[,1]<1) # this removes what seem to be central hepatocytes
end_cells$embolised = end_cells$embolised[,!outcells]
end_cells$embolised = end_cells$embolised[,end_cells$embolised@meta.data[,"endo_res.0.8"]!=12] # remove these extra stellate cells

end_cells$embolised = suppressWarnings(SCTransform(end_cells$embolised, do.correct.umi = T, 
                                                 verbose = F,
                                                 vars.to.regress = c("unique_name","nCount_RNA"),
                                                 variable.features.rv.th = 1, seed.use = 1,
                                                 return.only.var.genes = F, 
                                                 variable.features.n = NULL))
end_cells$embolised = RunPCA(end_cells$embolised, verbose = F)
end_cells$embolised = RunUMAP(end_cells$embolised, dims = 1:15, verbose = F)
DimPlot(end_cells$embolised, reduction = "umap", group.by = "unique_name")



outcells = (end_cells$regenerating@reductions$umap@cell.embeddings[,2]>2.7 & 
              end_cells$regenerating@reductions$umap@cell.embeddings[,1]<(-1)) # this removes what seem to be central hepatocytes
end_cells$regenerating = end_cells$regenerating[,!outcells]

end_cells$regenerating = suppressWarnings(SCTransform(end_cells$regenerating, do.correct.umi = T, 
                                                 verbose = F,
                                                 vars.to.regress = c("unique_name","nCount_RNA"),
                                                 variable.features.rv.th = 1, seed.use = 1,
                                                 return.only.var.genes = F, 
                                                 variable.features.n = NULL))
end_cells$regenerating = RunPCA(end_cells$regenerating, verbose = F)
end_cells$regenerating = RunUMAP(end_cells$regenerating, dims = 1:15, verbose = F)
DimPlot(end_cells$regenerating, reduction = "umap", group.by = "unique_name")

Calculate correlations

sig_genes = gene_fit_p[gene_fit_p<=0.05]
top_sig_genes = names(sig_genes[order(sig_genes, decreasing = F)][1:1000])
top_sig_genes = top_sig_genes[top_sig_genes %in% rownames(end_cells$embolised@assays$SCT@data) &
                                top_sig_genes %in%  rownames(end_cells$regenerating@assays$SCT@data)]

data_gam_h = cbind(data.frame("DPT" = df_dc$DPT),
                   Matrix::t(end_cells$healthy@assays$SCT@data[top_sig_genes,]))
data_gam_h$DPT = scales::rescale(rank(-data_gam_h$DPT), c(0.00001, 0.99999))

gam_healthy = gam::gam(DPT ~ ., data = data_gam_h, family = mgcv::betar(link = "logit", eps = 0.00001))

hea_pred = predict(gam_healthy,
                   Matrix::t(end_cells$health@assays$SCT@data[top_sig_genes,]), type= "response")
reg_pred = predict(gam_healthy,
                   Matrix::t(end_cells$regenerating@assays$SCT@data[top_sig_genes,]), type= "response")
emb_pred = predict(gam_healthy,
                   Matrix::t(end_cells$embolised@assays$SCT@data[top_sig_genes,]), type= "response")

plot(density(data_gam_h$DPT), col = "green", ylim = c(0,2.75))
lines(density(reg_pred), col = "blue")
lines(density(emb_pred), col = "red")
lines(density(hea_pred), col = "grey50")
legend(-0.073,32, legend = c("healthy (DC1)", "healthy (pred)", 
                         "embolised (pred)", "regenerating (pred)"),
       fill = c("green", "grey50", "red", "blue"))


plot_df = data.frame(vals = c(data_gam_h$DPT, emb_pred, reg_pred), 
                     Condition = c(rep("healthy", length(data_gam_h$DPT)), 
                                   rep("embolised", length(emb_pred)), 
                                   rep("regenerating", length(reg_pred))),
                     Donor = c(end_cells$healthy$Donor, 
                               end_cells$embolised$Donor, end_cells$regenerating$Donor),
                     ct = c(end_cells$healthy$allcells_simp, 
                               end_cells$embolised$allcells_simp, end_cells$regenerating$allcells_simp))
plot_df$bins100 = cut(plot_df$vals, 10)
plot_df$Condition = factor(plot_df$Condition, levels = c("healthy", "embolised", "regenerating"))
ggplot(plot_df, aes(x = bins100, fill = Condition))+
     geom_bar(position = "fill")+
     labs(x = "zonation (binned)", y = "proportion")+
     scale_y_continuous(expand = c(0,0))+
     scale_fill_manual(values = colcond)+
     guides(fill = guide_legend(title.position = "top"))+
     theme(axis.text.x = element_blank(),
           legend.title.align = 0,
           legend.position = "bottom")

ggplot(plot_df, aes(x = bins100, fill = Condition))+
     facet_wrap(~Condition)+
     geom_bar()+
     labs(x = "zonation (binned)", y = "Number of cells")+
     scale_y_continuous(expand = c(0,0))+
     scale_fill_manual(values = colcond)+
     coord_flip()+
     guides(fill = guide_legend(title.position = "top"))+
     theme(axis.text.y = element_blank(),
           legend.title.align = 0,
           legend.position = "bottom")

ggplot(plot_df, aes(x = vals, colour = Donor))+
     facet_wrap(~Condition)+
     geom_density()+
     labs(x = "zonation (binned)", y = "proportion")+
     theme_bw()+
     theme(legend.title.align = 0,
           legend.position = "bottom")

ggplot(plot_df, aes(x = vals, colour = ct))+
     facet_wrap(~Condition)+
     geom_density()+
     labs(x = "zonation (binned)", y = "proportion")+
     theme_bw()+
     theme(legend.title.align = 0,
           legend.position = "bottom")


reg_df = data.frame("pt" = reg_pred, "don" = end_cells$regenerating$Donor)
reg_df = cbind(reg_df, Matrix::t(end_cells$regenerating@assays$SCT@data[gfresh,]))
ggplot(reg_df, aes(x = pt, y = CLEC1B, colour = don))+
  geom_point()+
  theme_classic()+
  facet_wrap(~don)+
  geom_hline(yintercept = 3)+
  geom_vline(xintercept = c(0,1))


ggplot(reg_df, aes(x = CLEC1B, y = LYVE1, colour = don))+
  geom_point()+
  theme_classic()+
  facet_wrap(~don)


traj_list = list("healthy" = data_gam_h$DPT, "embolised" = emb_pred, "regenerating" = reg_pred)

Find varying genes - using all cells along the ranked pseudotime

cor_h = t(cor(data_gam_h$DPT, as.matrix(Matrix::t(end_cells$healthy@assays$SCT@data)),
              method = "sp"))
cor_emb = t(cor(emb_pred, as.matrix(Matrix::t(end_cells$embolised@assays$SCT@data)),
                method = "sp"))
cor_reg = t(cor(reg_pred, as.matrix(Matrix::t(end_cells$regenerating@assays$SCT@data)),
                method = "sp"))

l = list(cor_h, cor_emb, cor_reg)
all_corr = Reduce(function(x, y) merge(x,y, by = "rn", all = T), 
                  lapply(l, function(x) data.frame(x, rn = row.names(x))))
colnames(all_corr) = c("genes", "healthy", "embolised", "regenerating")

write.csv(all_corr, file = paste0("results/zonation_cond/end_correlations_zonation_rank.csv"),
          col.names = T, row.names = F, quote = F)
attempt to set 'col.names' ignored

Save trajectories and genes

save(fits_list, qvals_list, traj_list, 
     file = "results/zonation_cond/end_traj_qval_fits_rank.RData")

Find varying genes - normalised by bins along the ranked pseudotime

qvals_b_list = list()
fits_b_list = list()

# get HVG from all conditions
hvg = unique(c(end_cells[["healthy"]]@assays$SCT@var.features,
               end_cells[["embolised"]]@assays$SCT@var.features,
               end_cells[["regenerating"]]@assays$SCT@var.features))
hvg = hvg[hvg %in% rownames(end_cells[["healthy"]]@assays$SCT@data) &
            hvg %in% rownames(end_cells[["embolised"]]@assays$SCT@data) &
            hvg %in% rownames(end_cells[["regenerating"]]@assays$SCT@data)]

for(cond in names(end_cells)){
  print(cond)
  set.seed(1)
  # filtering the cells by bins
  bins = cut(traj_list[[cond]], 100)
  dons = end_cells[[cond]]$Donor
  ndons = rowSums(table(bins, dons)>1)
  ncells = rowSums(table(bins, dons))
  sampleif = function(cells, bins, dons){
    res = c()
    for(d in unique(dons)){
      for(b in unique(bins)){
        x = cells[bins==b & dons==d]
        if(length(x)>=30){
          res = c(res, sample(x, 30, replace = F))
        } else if(length(x)>=1 & ndons[b]>=2 & ncells[b]>=10){
          res = c(res, x)
        } else{
          res = c(res, NULL)
        }
      }
    }
    return(res)
  }
  whichcells = sampleif(1:length(bins), bins, dons)
  
  # Fit GAM for each gene using pseudotime as independent variable.
  t = traj_list[[cond]][whichcells]
  gene_fit_p = c()
  gene_fit_vals = data.frame(row.names = 1:100)
  for(i in 1:length(hvg)){
    g = hvg[i]
    z = end_cells[[cond]]@assays$SCT@data[g,whichcells]
    
    d = data.frame(z=z, t=t)
    tmp = suppressMessages(gam(z ~ ns(t, df = 3), data=d))
    
    # bins for model fitting
    bb = seq(min(t), max(t), length.out = 100)
    gene_fit_vals[,g] = suppressMessages(predict(tmp,
                                                 newdata = data.frame(t = bb)))
    p = summary(tmp)$parametric.anova$`Pr(>F)`[1]
    gene_fit_p = c(gene_fit_p, p)
    names(gene_fit_p)[length(gene_fit_p)] = g
  }
  
  if(sum(is.na(gene_fit_p))>0){
    gene_fit_p[is.na(gene_fit_p)] = 1
  }
  gene_fit_p = fdrtool::fdrtool(gene_fit_p, statistic="pvalue", plot=F, 
                                verbose=F, cutoff.method="pct0", pct0=0.9)$qval
  
  qvals_b_list[[cond]] = gene_fit_p
  fits_b_list[[cond]] = gene_fit_vals
}

Save trajectories and genes

save(fits_b_list, qvals_b_list, traj_list, 
     file = "results/zonation_cond/end_binned_traj_qval_fits_rank.RData")

Find varying genes - using each donor as a replicate and calculating the average expression for each of 100 bins

save(fits_b_list, qvals_b_list, traj_list, 
     file = "results/zonation_cond/end_binned_traj_qval_fits_rank.RData")

Save trajectories and genes

qvals_db_list = list()
fits_db_list = list()

# get HVG from all conditions
hvg = unique(c(end_cells[["healthy"]]@assays$SCT@var.features,
               end_cells[["embolised"]]@assays$SCT@var.features,
               end_cells[["regenerating"]]@assays$SCT@var.features))
hvg = hvg[hvg %in% rownames(end_cells[["healthy"]]@assays$SCT@data) &
            hvg %in% rownames(end_cells[["embolised"]]@assays$SCT@data) &
            hvg %in% rownames(end_cells[["regenerating"]]@assays$SCT@data)]

meta_df = data.frame("pt" = c(traj_list$healthy, traj_list$embolised, traj_list$regenerating),
                     "donors" = c(end_cells$healthy$Donor, end_cells$embolised$Donor,
                                  end_cells$regenerating$Donor),
                     "cond" = c(end_cells$healthy$Condition, end_cells$embolised$Condition,
                                  end_cells$regenerating$Condition))
meta_df$bins = cut(meta_df$pt, 100)

for(cond in unique(meta_df$cond)){
  print(cond)
  set.seed(1)
  
  # Fit GAM for each gene
  t = 1:100
  sub_meta_df = meta_df[meta_df$cond==cond,]
  gene_fit_p = c()
  gene_fit_vals = data.frame(row.names = 1:100)
  for(i in 1:length(hvg)){
    g = hvg[i]
    z = end_cells[[cond]]@assays$SCT@data[g,]
    
    d = aggregate(z~bins+donors, data = cbind(sub_meta_df, z), FUN="mean")
    cc = data.frame(table(sub_meta_df$bins, as.factor(as.character(sub_meta_df$donors))))
    cc = cc[cc$Freq==0,]
    colnames(cc) = colnames(d)
    d = rbind(d, cc)
    d = d[order(d$bins, decreasing = F),]
    d$t = rep(t, each = length(unique(d$donors)))
    tmp = suppressMessages(gam(z ~ ns(t, df = 3), data=d))
    
    # bins for model fitting
    bb = seq(min(t), max(t), length.out = 100)
    gene_fit_vals[,g] = suppressMessages(predict(tmp,
                                                 newdata = data.frame(t = bb)))
    p = summary(tmp)$parametric.anova$`Pr(>F)`[1]
    gene_fit_p = c(gene_fit_p, p)
    names(gene_fit_p)[length(gene_fit_p)] = g
  }
  
  if(sum(is.na(gene_fit_p))>0){
    gene_fit_p[is.na(gene_fit_p)] = 1
  }
  gene_fit_p = fdrtool::fdrtool(gene_fit_p, statistic="pvalue", plot=F, 
                                verbose=F, cutoff.method="pct0", pct0=0.9)$qval
  
  qvals_db_list[[cond]] = gene_fit_p
  fits_db_list[[cond]] = gene_fit_vals
}
[1] "healthy"
[1] "embolised"
[1] "regenerating"

Plot cell distributions in pseudotime

save(fits_db_list, qvals_db_list, traj_list, 
     file = "results/zonation_cond/end_binnedDonor_traj_qval_fits_rank.RData")

Do cross validation on the healthy trajectory

plot_df = data.frame("pseudotime" = c(traj_list$healthy, traj_list$embolised,
                                      traj_list$regenerating),
           "donor" = c(end_cells$healthy@meta.data[,"Donor"],
                       end_cells$embolised@meta.data[names(traj_list$embolised),"Donor"],
                       end_cells$regenerating@meta.data[names(traj_list$regenerating),"Donor"]),
           "cond" = c(rep("healthy", length(traj_list$healthy)), 
                      rep("embolised", length(traj_list$embolised)),
                      rep("regenerating", length(traj_list$regenerating))))

pdf("results/zonation_cond/end_cell_distributions_zonation.pdf", useDingbats = F, 
    width = 6, height = 5)
ggplot(plot_df, aes(x = donor, y = pseudotime, fill = cond))+
  geom_boxplot(notch = T)+
  theme_bw()

ggplot(plot_df, aes(x = pseudotime, group = donor, colour = donor))+
  facet_wrap(~cond)+
  geom_density()+
  theme_bw()

ggplot(plot_df, aes(x = pseudotime, colour = cond))+
  geom_density()+
  theme_bw()
dev.off()
null device 
          1 

Save zonation results

sig_genes = qvals_b_list$healthy[qvals_b_list$healthy<=0.05]
top_sig_genes = names(sig_genes[order(sig_genes, decreasing = F)][1:1000]) 
# 500 in this case actually seems to be one of the models with the lowest MSE

data_gam_h = cbind(data.frame("DPT" = traj_list$healthy),
                   Matrix::t(end_cells$healthy@assays$SCT@data[top_sig_genes,]))
colnames(data_gam_h) = gsub("-", "_", colnames(data_gam_h), fixed = T)
colnames(data_gam_h) = gsub(".", "_", colnames(data_gam_h), fixed = T)

cv_gam = CVgam(data = data_gam_h, formula = DPT ~ ., nfold = 10, seed = 1, method = "glm.fit")
CV-mse-GAM  
     0.0111 
pdf("results/zonation_healthy/end_original_fitted_pseudotime.pdf", useDingbats = F, 
    width = 6, height = 5)
plot(data_gam_h$DPT, cv_gam$fitted, xlab = "Healthy pseudotime (DPT363)",ylab = "Predicted Healthy",
     main = paste0("Healthy original vs fitted pseudotime\nMSE: ", round(cv_gam$cvscale, 6)),
     pch = 19, cex = 0.5)
abline(0,1, col = "blue", lwd = 3)
dev.off()
null device 
          1 

Comparing genes between conditions

Load data

end_cells = readRDS(file = "results/zonation_cond/end_cells_zonation_rank.RDS")
hep_cells = readRDS(file = "results/zonation_cond/hep_cells_zonation_rank.RDS")

Load fits

end_cells = readRDS(file = "results/zonation_cond/end_cells_zonation_rank.RDS")
hep_cells = readRDS(file = "results/zonation_cond/hep_cells_zonation_rank.RDS")

Calculate expression per bins

load("results/zonation_cond/hep_binnedDonor_traj_qval_fits_rank.RData")
hep_fits_list = fits_db_list
hep_qvals_list = qvals_db_list
hep_traj = traj_list
load("results/zonation_cond/end_binnedDonor_traj_qval_fits_rank.RData")
end_fits_list = fits_db_list
end_qvals_list = qvals_db_list
end_traj = traj_list

Correlate binned expression

nbins = 100

dfhep_h = data.table("zonation_pt" = hep_traj$healthy)
dfhep_h$zonation_int = cut(dfhep_h$zonation_pt, nbins)
dfhep_e = data.table("zonation_pt" = hep_traj$embolised)
dfhep_e = dfhep_h[dfhep_e, on="zonation_pt", roll=Inf, rollends = T]
dfhep_r = data.table("zonation_pt" = hep_traj$regenerating)
dfhep_r = dfhep_h[dfhep_r, on="zonation_pt", roll=Inf, rollends = T]

hep_mean_bins = list(
healthy = apply(hep_cells$healthy@assays$SCT@data, 1, 
                        function(x) tapply(x, dfhep_h$zonation_int, mean)),
embolised = apply(hep_cells$embolised@assays$SCT@data, 1, 
                        function(x) tapply(x, dfhep_e$zonation_int, mean)),
regenerating = apply(hep_cells$regenerating@assays$SCT@data, 1, 
                        function(x) tapply(x, dfhep_r$zonation_int, mean))
)

Correlate fitted expression

comb_cond = combn(names(end_cells), 2)
hep_cor_fit_list = list()
for(i in 1:ncol(comb_cond)){
  c1 = comb_cond[1,i]
  c2 = comb_cond[2,i]
  
  genes = intersect(colnames(hep_fits_list[[c1]]),colnames(hep_fits_list[[c2]]))
  
  for(g in genes){
    hep_cor_fit_list[[paste0(c1,"_",c2)]][[g]] = cor(cbind(hep_fits_list[[c1]][,g],
                                                       hep_fits_list[[c2]][,g]),
                                                 method = "sp")[1,2]
  }
}
hep_cor_fit_list = lapply(hep_cor_fit_list, data.frame)
hep_cor_fit_list = lapply(hep_cor_fit_list, function(x) cbind(rownames(x), x))

end_cor_fit_list = list()
for(i in 1:ncol(comb_cond)){
  c1 = comb_cond[1,i]
  c2 = comb_cond[2,i]
  
  genes = intersect(colnames(end_fits_list[[c1]]),colnames(end_fits_list[[c2]]))
  
  for(g in genes){
    end_cor_fit_list[[paste0(c1,"_",c2)]][[g]] = cor(cbind(end_fits_list[[c1]][,g],
                                                       end_fits_list[[c2]][,g]),
                                                 method = "sp")[1,2]
  }
}
end_cor_fit_list = lapply(end_cor_fit_list, data.frame)
end_cor_fit_list = lapply(end_cor_fit_list, function(x) cbind(rownames(x), x))

Save correlations

save(hep_cor_list, end_cor_list, 
     file = "results/zonation_cond/zonation_cor_cond_lists_rank.RData")

Filter pathways with ribosomal genes

save(hep_cor_list, end_cor_list, 
     file = "results/zonation_cond/zonation_cor_cond_lists_rank.RData")

Hepatocytes

Get the top genes for each condition

rpfilter = function(x){
  return(!grepl("^RPL", x$geneID) & !grepl("^RPS", x$geneID) & 
           !grepl("/RPL", x$geneID, fixed = T) & !grepl("/RPS", x$geneID, fixed = T))
}

Find enriched GO Terms and Reactome pathways

# get signifficant genes showing variation (at least 0.1 range in the fitted values)
genes_list = list()
for(n in names(hep_fits_list)){
  h_fc_sp = apply(hep_fits_list[[n]], 2, function(x) diff(range(x)))
  genes_c = names(hep_qvals_list[[n]])[hep_qvals_list[[n]]<=0.05 &
                    names(hep_qvals_list[[n]]) %in% names(h_fc_sp)[h_fc_sp>=0.1]]
  genes_c = genes_c[genes_c %in% names(hep_qvals_list[[n]])[order(hep_qvals_list[[n]], decreasing = F)]]
  genes_list[[n]] = genes_c
}

Identify pathways more affected by one condition

anot_list = list()
for(n in names(terms_groups_list)){
  terms_groups = terms_groups_list[[n]]
  df_cor = df_cor_list[[n]]
  
  # get the pathways for all genes, minus those with ribosomal genes
  all_path = terms_groups$all[rpfilter(terms_groups$all),]
  all_path = all_path[order(all_path$qvalue, decreasing = F),]
  
  # get gene groups as a data frame
  g_g = unlist(apply(df_cor[,c(1,3,4,5)], 1, 
                     function(x) colnames(df_cor[,c(3,4,5)])[which(as.logical(x[2:4]))]))
  gene_group = data.frame("gene" = names(g_g),
                          "group" = g_g)
  
  # get fraction of genes from a condition that are present in a give pathway
  n_g_group = list()
  n_g_group[[levels(gene_group$group)[1]]] = c()
  n_g_group[[levels(gene_group$group)[2]]] = c()
  n_g_group[[levels(gene_group$group)[3]]] = c()
  for(i in 1:nrow(all_path)){
    genes_p = unlist(strsplit(all_path$geneID[i], "/"))
    for(gg in unique(gene_group$group)){
      ngg = sum(genes_p %in% gene_group$gene[gene_group$group==gg])/sum(gene_group$group==gg)
      n_g_group[[gg]] = c(n_g_group[[gg]], ngg)
    }
  }
  all_path = cbind(all_path, data.frame(n_g_group))
  all_path$diff = all_path[,13] - all_path[,12] # add difference between cond and healthy
  all_path$highest = colnames(all_path)[11:13][apply(all_path[,11:13], 1, which.max)]
  
  # group the detected pathways by similarity
  go_path = all_path[all_path$DB=="GO Term",]
  mat_path = matrix(0, nrow(go_path), nrow(go_path))
  for(i in 1:nrow(go_path)){
    g1 = go_path[i,"geneID"]
    g1 = unlist(strsplit(g1, "/"))
    for(j in 1:nrow(go_path)){
        g2 = go_path[j,"geneID"]
        g2 = unlist(strsplit(g2, "/"))
        mat_path[i,j] = length(intersect(g1, g2))/mean(c(length(g1), length(g2)))
    }
  }
  cl_go = hclust(dist(mat_path), method = "ward.D2")
  cl_go = cutree(cl_go, 7)
  rownames(mat_path) = go_path$Description
  anot = data.frame(row.names = go_path$Description, 
                    "cl" = paste0("cl",cl_go), 
                    "n" = go_path$Count,
                    "diff" = go_path$diff,
                    "qval" = -log10(go_path$qvalue),
                    "term" = go_path$Description)
  pheatmap::pheatmap(mat_path, clustering_method = "ward.D2", 
                     annotation_row = anot[,-5], show_rownames = F)
  genes_per_gocl = tapply(anot$term, anot$cl, 
         function(x) unique(unlist(strsplit(go_path[go_path$Description %in% x,"geneID"], "/"))))
  genes_cond_gocl = lapply(genes_per_gocl, function(x) lapply(genes_conds, function(y) sum(y %in% x)))
  genes_cond_gocl = Reduce(rbind, genes_cond_gocl)
  rownames(genes_cond_gocl) = paste0("cl", 1:7)
  
  anot_list[[n]] = anot
}

Plot top terms

anot_list = list()
for(n in names(terms_groups_list)){
  terms_groups = terms_groups_list[[n]]
  df_cor = df_cor_list[[n]]
  
  # get the pathways for all genes, minus those with ribosomal genes
  all_path = terms_groups$all[rpfilter(terms_groups$all),]
  all_path = all_path[order(all_path$qvalue, decreasing = F),]
  
  # get gene groups as a data frame
  g_g = unlist(apply(df_cor[,c(1,3,4,5)], 1, 
                     function(x) colnames(df_cor[,c(3,4,5)])[which(as.logical(x[2:4]))]))
  gene_group = data.frame("gene" = names(g_g),
                          "group" = g_g)
  
  # get fraction of genes from a condition that are present in a give pathway
  n_g_group = list()
  n_g_group[[levels(gene_group$group)[1]]] = c()
  n_g_group[[levels(gene_group$group)[2]]] = c()
  n_g_group[[levels(gene_group$group)[3]]] = c()
  for(i in 1:nrow(all_path)){
    genes_p = unlist(strsplit(all_path$geneID[i], "/"))
    for(gg in unique(gene_group$group)){
      ngg = sum(genes_p %in% gene_group$gene[gene_group$group==gg])/sum(gene_group$group==gg)
      n_g_group[[gg]] = c(n_g_group[[gg]], ngg)
    }
  }
  all_path = cbind(all_path, data.frame(n_g_group))
  all_path$diff = all_path[,13] - all_path[,12] # add difference between cond and healthy
  all_path$highest = colnames(all_path)[11:13][apply(all_path[,11:13], 1, which.max)]
  
  # group the detected pathways by similarity
  go_path = all_path[all_path$DB=="GO Term",]
  mat_path = matrix(0, nrow(go_path), nrow(go_path))
  for(i in 1:nrow(go_path)){
    g1 = go_path[i,"geneID"]
    g1 = unlist(strsplit(g1, "/"))
    for(j in 1:nrow(go_path)){
        g2 = go_path[j,"geneID"]
        g2 = unlist(strsplit(g2, "/"))
        mat_path[i,j] = length(intersect(g1, g2))/mean(c(length(g1), length(g2)))
    }
  }
  cl_go = hclust(dist(mat_path), method = "ward.D2")
  cl_go = cutree(cl_go, 7)
  rownames(mat_path) = go_path$Description
  anot = data.frame(row.names = go_path$Description, 
                    "cl" = paste0("cl",cl_go), 
                    "n" = go_path$Count,
                    "diff" = go_path$diff,
                    "qval" = -log10(go_path$qvalue),
                    "term" = go_path$Description)
  pheatmap::pheatmap(mat_path, clustering_method = "ward.D2", 
                     annotation_row = anot[,-5], show_rownames = F)
  genes_per_gocl = tapply(anot$term, anot$cl, 
         function(x) unique(unlist(strsplit(go_path[go_path$Description %in% x,"geneID"], "/"))))
  genes_cond_gocl = lapply(genes_per_gocl, function(x) lapply(genes_conds, function(y) sum(y %in% x)))
  genes_cond_gocl = Reduce(rbind, genes_cond_gocl)
  rownames(genes_cond_gocl) = paste0("cl", 1:7)
  
  anot_list[[n]] = anot
}

Plot some genes

pdf("results/zonation_cond/hep_top_go_terms_genes.pdf", 
    height = 6, width = 7, useDingbats = F)
for(n in names(terms_groups_list)){
  terms_groups = terms_groups_list[[n]]
  
  for(cond in names(terms_groups)){
    plot_df = terms_groups[[cond]][rpfilter(terms_groups[[cond]]),]
    plot_df = plot_df[order(plot_df$qvalue, decreasing = F),]
    plot_df$Description = factor(plot_df$Description, levels = rev(plot_df$Description))
    
    plt = ggplot(plot_df[1:10,], aes(x = Description, y = -log10(qvalue)))+
      geom_bar(stat = "identity")+
      coord_flip()+
      labs(title = paste0(n, ": ", cond))+
      theme_bw()
    print(plt)
  }
}
dev.off()
null device 
          1 
pathway = "negative regulation of apoptotic signaling pathway"
unique(exp_plt_list$healthy_regenerating$regenerating[[pathway]]$variable)
gn = "ICAM1"
plot_df = exp_plt_list$healthy_regenerating$regenerating[[pathway]]
plot_df = plot_df[plot_df$variable==gn,]
c1 = "healthy"
c2 = "regenerating"
pw = "regenerating"
ggplot(plot_df, aes(x = xx, y = value, group = interaction(variable, cond)))+
    geom_line(mapping = aes(colour = cond))+
    labs(title = pathway, subtitle = paste0(c1, " vs ", c2, ", ", pw, " pathways"), 
         y = paste0("log(norm exp) ", gn))+
    theme_classic()

Save important lists

save(terms_groups_list, anot_list, df_cor_list, genes_list, plot_list, exp_plt_list,
     file = "results/zonation_cond/hep_go_results_rank.RData")

Endothelial cells

Get the top genes for each condition

save(terms_groups_list, anot_list, df_cor_list, genes_list, plot_list, exp_plt_list,
     file = "results/zonation_cond/hep_go_results_rank.RData")

Find enriched GO Terms and Reactome pathways

# get signifficant genes showing variation (at least 0.1 range in the fitted values)
genes_list = list()
for(n in names(end_fits_list)){
  h_fc_sp = apply(end_fits_list[[n]], 2, function(x) diff(range(x)))
  genes_c = names(end_qvals_list[[n]])[end_qvals_list[[n]]<=0.05 &
                    names(end_qvals_list[[n]]) %in% names(h_fc_sp)[h_fc_sp>=0.1]]
  genes_c = genes_c[genes_c %in% names(end_qvals_list[[n]])[order(end_qvals_list[[n]], decreasing = F)]]
  genes_list[[n]] = genes_c
}

Identify pathways more affected by one condition

anot_list = list()
for(n in names(terms_groups_list)){
  terms_groups = terms_groups_list[[n]]
  df_cor = df_cor_list[[n]]
  
  # get the pathways for all genes, minus those with ribosomal genes
  all_path = terms_groups$all[rpfilter(terms_groups$all),]
  all_path = all_path[order(all_path$qvalue, decreasing = F),]
  
  # get gene groups as a data frame
  g_g = unlist(apply(df_cor[,c(1,3,4,5)], 1, 
                     function(x) colnames(df_cor[,c(3,4,5)])[which(as.logical(x[2:4]))]))
  gene_group = data.frame("gene" = names(g_g),
                          "group" = g_g)
  
  # get fraction of genes from a condition that are present in a give pathway
  n_g_group = list()
  n_g_group[[levels(gene_group$group)[1]]] = c()
  n_g_group[[levels(gene_group$group)[2]]] = c()
  n_g_group[[levels(gene_group$group)[3]]] = c()
  for(i in 1:nrow(all_path)){
    genes_p = unlist(strsplit(all_path$geneID[i], "/"))
    for(gg in unique(gene_group$group)){
      ngg = sum(genes_p %in% gene_group$gene[gene_group$group==gg])/sum(gene_group$group==gg)
      n_g_group[[gg]] = c(n_g_group[[gg]], ngg)
    }
  }
  all_path = cbind(all_path, data.frame(n_g_group))
  all_path$diff = all_path[,13] - all_path[,12] # add difference between cond and healthy
  all_path$highest = colnames(all_path)[11:13][apply(all_path[,11:13], 1, which.max)]
  
  # group the detected pathways by similarity
  go_path = all_path[all_path$DB=="GO Term",]
  mat_path = matrix(0, nrow(go_path), nrow(go_path))
  for(i in 1:nrow(go_path)){
    g1 = go_path[i,"geneID"]
    g1 = unlist(strsplit(g1, "/"))
    for(j in 1:nrow(go_path)){
        g2 = go_path[j,"geneID"]
        g2 = unlist(strsplit(g2, "/"))
        mat_path[i,j] = length(intersect(g1, g2))/mean(c(length(g1), length(g2)))
    }
  }
  cl_go = hclust(dist(mat_path), method = "ward.D2")
  cl_go = cutree(cl_go, 7)
  rownames(mat_path) = go_path$Description
  anot = data.frame(row.names = go_path$Description, 
                    "cl" = paste0("cl",cl_go), 
                    "n" = go_path$Count,
                    "diff" = go_path$diff,
                    "qval" = -log10(go_path$qvalue),
                    "term" = go_path$Description)
  pheatmap::pheatmap(mat_path, clustering_method = "ward.D2", 
                     annotation_row = anot[,-5], show_rownames = F)
  genes_per_gocl = tapply(anot$term, anot$cl, 
         function(x) unique(unlist(strsplit(go_path[go_path$Description %in% x,"geneID"], "/"))))
  genes_cond_gocl = lapply(genes_per_gocl, function(x) lapply(genes_conds, function(y) sum(y %in% x)))
  genes_cond_gocl = Reduce(rbind, genes_cond_gocl)
  rownames(genes_cond_gocl) = paste0("cl", 1:7)
  
  anot_list[[n]] = anot
}

Plot top terms

anot_list = list()
for(n in names(terms_groups_list)){
  terms_groups = terms_groups_list[[n]]
  df_cor = df_cor_list[[n]]
  
  # get the pathways for all genes, minus those with ribosomal genes
  all_path = terms_groups$all[rpfilter(terms_groups$all),]
  all_path = all_path[order(all_path$qvalue, decreasing = F),]
  
  # get gene groups as a data frame
  g_g = unlist(apply(df_cor[,c(1,3,4,5)], 1, 
                     function(x) colnames(df_cor[,c(3,4,5)])[which(as.logical(x[2:4]))]))
  gene_group = data.frame("gene" = names(g_g),
                          "group" = g_g)
  
  # get fraction of genes from a condition that are present in a give pathway
  n_g_group = list()
  n_g_group[[levels(gene_group$group)[1]]] = c()
  n_g_group[[levels(gene_group$group)[2]]] = c()
  n_g_group[[levels(gene_group$group)[3]]] = c()
  for(i in 1:nrow(all_path)){
    genes_p = unlist(strsplit(all_path$geneID[i], "/"))
    for(gg in unique(gene_group$group)){
      ngg = sum(genes_p %in% gene_group$gene[gene_group$group==gg])/sum(gene_group$group==gg)
      n_g_group[[gg]] = c(n_g_group[[gg]], ngg)
    }
  }
  all_path = cbind(all_path, data.frame(n_g_group))
  all_path$diff = all_path[,13] - all_path[,12] # add difference between cond and healthy
  all_path$highest = colnames(all_path)[11:13][apply(all_path[,11:13], 1, which.max)]
  
  # group the detected pathways by similarity
  go_path = all_path[all_path$DB=="GO Term",]
  mat_path = matrix(0, nrow(go_path), nrow(go_path))
  for(i in 1:nrow(go_path)){
    g1 = go_path[i,"geneID"]
    g1 = unlist(strsplit(g1, "/"))
    for(j in 1:nrow(go_path)){
        g2 = go_path[j,"geneID"]
        g2 = unlist(strsplit(g2, "/"))
        mat_path[i,j] = length(intersect(g1, g2))/mean(c(length(g1), length(g2)))
    }
  }
  cl_go = hclust(dist(mat_path), method = "ward.D2")
  cl_go = cutree(cl_go, 7)
  rownames(mat_path) = go_path$Description
  anot = data.frame(row.names = go_path$Description, 
                    "cl" = paste0("cl",cl_go), 
                    "n" = go_path$Count,
                    "diff" = go_path$diff,
                    "qval" = -log10(go_path$qvalue),
                    "term" = go_path$Description)
  pheatmap::pheatmap(mat_path, clustering_method = "ward.D2", 
                     annotation_row = anot[,-5], show_rownames = F)
  genes_per_gocl = tapply(anot$term, anot$cl, 
         function(x) unique(unlist(strsplit(go_path[go_path$Description %in% x,"geneID"], "/"))))
  genes_cond_gocl = lapply(genes_per_gocl, function(x) lapply(genes_conds, function(y) sum(y %in% x)))
  genes_cond_gocl = Reduce(rbind, genes_cond_gocl)
  rownames(genes_cond_gocl) = paste0("cl", 1:7)
  
  anot_list[[n]] = anot
}

Plot some genes

# plot all genes from pathway in grey, average in colour for cond and healthy
plot_list = list()
exp_plt_list = list()
for(comp in names(terms_groups_list)){
  print(comp)
  c1 = strsplit(comp, "_")[[1]][1]
  c2 = strsplit(comp, "_")[[1]][2]
  
  plot_list[[comp]] = list()
  exp_plt_list[[comp]] = list()
  for(pw in c(c1, c2)){
    goterms = terms_groups_list[[comp]][[pw]]
    goterms = goterms[goterms$DB=="GO Term" & rpfilter(goterms),]
    
    exp_plt_list[[comp]][[pw]] = list()
    plot_list[[comp]][[pw]] = list()
    if(nrow(goterms)>0){
      for(go in 1:nrow(goterms)){
        genes_path = unlist(strsplit(goterms[go,"geneID"],"/"))
        
        exp_healthy = reshape2::melt(end_fits_list[[c1]][,genes_path])
        exp_healthy$xx = rep(1:100, length(genes_path))
        exp_healthy$cond = c1
        
        exp_cond = reshape2::melt(end_fits_list[[c2]][,genes_path])
        exp_cond$xx = rep(1:100, length(genes_path))
        exp_cond$cond = c2
  
        exp_df = rbind(exp_healthy, exp_cond)
  
        plt = ggplot(exp_df, aes(x = xx, y = value, group = interaction(variable, cond)))+
          #geom_line(mapping = aes(colour = cond), alpha = 0.2)+
          stat_smooth(mapping = aes(group = cond, colour = cond), show.legend = T)+
          labs(title = goterms[go,"Description"], 
               subtitle = paste0(c1, " vs ", c2, ", ", pw, " pathways"))+
          theme_classic()
        
        plot_list[[comp]][[pw]][[goterms[go,"Description"]]] = plt
        exp_plt_list[[comp]][[pw]][[goterms[go,"Description"]]] = exp_df
      }
    }
  }
}
pathway = "cellular response to type I interferon"
unique(exp_plt_list$healthy_regenerating$regenerating[[pathway]]$variable)
gn = "EGR1"
plot_df = exp_plt_list$healthy_regenerating$regenerating[[pathway]]
plot_df = plot_df[plot_df$variable==gn,]
c1 = "healthy"
c2 = "regenerating"
pw = "regenerating"
ggplot(plot_df, aes(x = xx, y = value, group = interaction(variable, cond)))+
    geom_line(mapping = aes(colour = cond))+
    labs(title = pathway, subtitle = paste0(c1, " vs ", c2, ", ", pw, " pathways"), 
         y = paste0("log(norm exp) ", gn))+
    theme_classic()

Save important lists

save(terms_groups_list, anot_list, df_cor_list, plot_list, exp_plt_list,
     file = "results/zonation_cond/end_go_results_rank.RData")

OLD CODE

Find genes with differencial variation - not very interesting since most genes seem to be showing a variation

save(terms_groups_list, anot_list, df_cor_list, plot_list, exp_plt_list,
     file = "results/zonation_cond/end_go_results_rank.RData")

DTW condition vs condition

cond_comb = combn(names(qvals_list), 2)
for(i in 1:ncol(cond_comb)){
  genes = intersect(names(qvals_list[[cond_comb[1,i]]])[qvals_list[[cond_comb[1,i]]]<=0.05],
                    names(qvals_list[[cond_comb[2,i]]])[qvals_list[[cond_comb[2,i]]]<=0.05])
  
  sc_list = list()
  for(cond in cond_comb[,i]){
    gene_fit_vals = fits_list[[cond]]
    gene_fit_vals_filt = t(gene_fit_vals[,genes])
    sc_list[[cond]] = t(apply(gene_fit_vals_filt, 1, scale))
  }
  
  xxx = dtw(sc_list[[1]], sc_list[[2]], window.type = "itakura", dist.method = "DTW")
  yyy = dtw(dist(t(sc_list[[1]]), t(sc_list[[2]]), method = "DTW"), window.type = "itakura")
}
source("aux_scripts/primate_cerebral_organoids_pt_alignment.r")

xcv = align_pt_traj(expr_ref = hep_cells$healthy@assays$SCT@data[genes,], 
                    pt_ref = traj_list$healthy,
                    expr_query = hep_cells$embolised@assays$SCT@data[genes,], 
                    pt_query = traj_list$embolised, num_breaks_ref = 100, 
                    ref_name = "healthy", query_name = "embolised")

plot(traj_list$embolised, xcv$pt_query_aligned)
abline(0,1)

xcb = align_pt_traj(expr_ref = hep_cells$healthy@assays$SCT@data[genes,], 
                    pt_ref = traj_list$healthy,
                    expr_query = hep_cells$regenerating@assays$SCT@data[genes,], 
                    pt_query = traj_list$regenerating, num_breaks_ref = 100, 
                    ref_name = "healthy", query_name = "regenerating")

plot(traj_list$regenerating, xcb$pt_query_aligned)
abline(0,1)
g = "CLEC1B"
plt_df = data.frame("traj" = c(traj_list$regenerating, traj_list$healthy),
           "exp" = c(end_cells$regenerating@assays$SCT@data[g,names(traj_list$regenerating)],
           end_cells$healthy@assays$SCT@data[g,]),
           "cond" = c(rep("regenerating", length(traj_list$regenerating)),
                      rep("healthy", length(traj_list$healthy))),
           "cl" = c(end_cells$regenerating@meta.data$allcells_simp,
                    end_cells$healthy@meta.data$allcells_simp))
plt_df = plt_df[order(plt_df$traj, decreasing = T),]

ggplot(plt_df, aes(x = traj, y = exp, group = cond, colour = cond))+
  geom_point()+
  stat_smooth()+
  labs(title = g)+
  theme_bw()

ggplot(plt_df, aes(x = traj, y = exp, group = cond, colour = cl, shape = cond))+
  geom_point()+
  stat_smooth()+
  labs(title = g)+
  theme_bw()

xxx = dtw::dtw(x = plt_df[plt_df$cond=="healthy","exp"], y = plt_df[plt_df$cond=="regenerating","exp"])
plot(xxx)
abline(0,1)

hti = cut_number(plt_df$traj[plt_df$cond=="healthy"], 100)
rti = cut_number(plt_df$traj[plt_df$cond=="regenerating"], 100)
df2 = data.frame("r" = tapply(plt_df$exp[plt_df$cond=="regenerating"], rti, mean),
                 "h" = tapply(plt_df$exp[plt_df$cond=="healthy"], hti, mean))
yyy = dtw::dtw(x = df2$h, y = df2$r)
plot(yyy)
abline(0,1)

Get GO Term enrichment

library(topGO)
cor_lists = list("hep" = hep_cor_list, "end" = end_cor_list)

cc = -0.25
go_enr_list = list()
for(gr in names(cor_lists)){
  cor_list = cor_lists[[gr]]

  go_enr_list[[gr]] = list()
  for(comp in names(cor_list)){
    testdf = cor_list[[comp]]
    
    
    
    ncond = strsplit(comp, "_")[[1]]
    go_enr_list[[gr]][[comp]] = list()
    for(cond in ncond){
      gsig = names(hep_qval[[cond]])[hep_qval[[cond]]<=0.05]
      
      subtestdf = merge(testdf, hep_qval[[cond]], by = 0)
      
      genes = subtestdf$X..i..
      names(genes) = subtestdf[,1]
      
      # GO enrichment
      fsel = function(x) return(x<cc & subtestdf$y<=0.05)
      
      sampleGOdata <- new("topGOdata",
                          description = "test", ontology = "BP",
                          allGenes = genes, geneSel = fsel,
                          nodeSize = 5, annot = annFUN.org, 
                          mapping = "org.Hs.eg.db", ID = "symbol")
      
      resultKS.elim <- runTest(sampleGOdata, algorithm = "elim", 
                               statistic = "fisher")
      
      resGO = GenTable(object = sampleGOdata, elimKS = resultKS.elim,
                       topNodes = resultKS.elim@geneData[4], numChar = 1000)
      resGO$elimKS = score(resultKS.elim)[resGO$GO.ID]
      resGO$cond = cond
      
      # get genes
      relevant_genes = sampleGOdata@allGenes[sampleGOdata@geneSelectionFun(sampleGOdata@allScores)]
      goGenes = t(data.frame(lapply(genesInTerm(sampleGOdata), function(x) paste(x[x %in% relevant_genes], collapse = ", "))))
      rownames(goGenes) = gsub(".", ":", rownames(goGenes), fixed = T)
      colnames(goGenes) = "genes"
      resGO = merge(resGO, goGenes, by.x = 1, by.y = 0, all.x = T)
      
      if(cond==ncond[[1]]){
        go_enr_list[[gr]][[comp]] = resGO[,c("GO.ID","Term","Annotated","Significant",
                                             "Expected","elimKS","cond","genes")]
      } else{
        go_enr_list[[gr]][[comp]] = rbind(go_enr_list[[gr]][[comp]],
                                          resGO[,c("GO.ID","Term","Annotated","Significant",
                                                   "Expected","elimKS","cond","genes")])
      }
      
    }
  }
}
saveRDS(go_enr_list, file = "results/zonation_cond/go_enr_corsig_list.RDS")
gg = unique(c(names(end_qval$healthy)[order(end_qval$healthy, decreasing = F)][1:1000],
              names(end_qval$regenerating)[order(end_qval$regenerating, decreasing = F)][1:1000]))
diff_fits = apply(end_fits$healthy[,gg], 2, rank)-apply(end_fits$regenerating[,gg], 2, rank)
cl = hclust(dist(t(diff_fits)), method = "ward.D")
ct = cutree(cl, 6)
df = data.frame("cor" = end_cor_fit_list$healthy_regenerating[gg,2],
                 "score" = apply(apply(t(diff_fits), 1, range), 
                                 2, function(x) sum(abs(x))))
diff_genes = rownames(df)[df$cor<0.25]
cl = hclust(dist(t(diff_fits)[diff_genes,]), method = "ward.D")
ct = cutree(cl, 4)
df = data.frame("cl" = factor(ct),
                "cor" = 1-end_cor_fit_list$healthy_regenerating[diff_genes,2],
                 "score" = apply(apply(t(diff_fits)[diff_genes,], 1, range), 
                                 2, function(x) sum(abs(x))))
pheatmap::pheatmap(t(diff_fits)[diff_genes,], cluster_cols = F, 
                   clustering_method = "ward.D", fontsize_row = 8, annotation_row = df)

plot(hep_fits$healthy[,"SAA2"])
plot(hep_fits$regenerating[,"SAA2"])
fsel = function(x) return(x<0.25 & names(genes) %in% gg)
fsel = function(x) return(names(genes) %in% gg)
genes = end_cor_fit_list$healthy_regenerating$X..i..
names(genes) = end_cor_fit_list$healthy_regenerating[,1]
genes = genes[!is.na(genes)]

eg = clusterProfiler::bitr(names(genes[fsel(genes)]), fromType="SYMBOL", 
                           toType="ENTREZID", OrgDb="org.Hs.eg.db")
eg_all = clusterProfiler::bitr(names(genes), fromType="SYMBOL", 
                           toType="ENTREZID", OrgDb="org.Hs.eg.db")

x <- ReactomePA::enrichPathway(gene=eg$ENTREZID,pvalueCutoff=0.05, 
                               readable=T, universe = eg_all$ENTREZID)
clusterProfiler::dotplot(x)
head(as.data.frame(x))

ego3 <- clusterProfiler::enrichGO(gene = eg$ENTREZID, universe = eg_all$ENTREZID, 
                               OrgDb = org.Hs.eg.db, ont = "BP", pvalueCutoff  = 0.01,
                               qvalueCutoff  = 0.05, 
                               pAdjustMethod = "BH", readable = T)
clusterProfiler::dotplot(ego3)
gg = unique(c(names(qvals_b_list$healthy)[order(qvals_b_list$healthy, decreasing = F)][1:1000],
              names(qvals_b_list$regenerating)[order(qvals_b_list$regenerating, decreasing = F)][1:1000]))
diff_fits = apply(fits_b_list$healthy[,gg], 2, rank)-apply(fits_b_list$regenerating[,gg], 2, rank)
cl = hclust(dist(t(diff_fits)), method = "ward.D")
ct = cutree(cl, 6)
df = data.frame("cor" = hep_cor_fit_list$healthy_regenerating[gg,2],
                 "score" = apply(apply(t(diff_fits), 1, range), 
                                 2, function(x) sum(abs(x))))
df = df[complete.cases(df),]
diff_genes = rownames(df)[df$cor<0.25]
cl = hclust(dist(t(diff_fits)[diff_genes,]), method = "ward.D")
ct = cutree(cl, 4)
df = data.frame("cl" = factor(ct),
                "cor" = 1-hep_cor_fit_list$healthy_regenerating[diff_genes,2],
                 "score" = apply(apply(t(diff_fits)[diff_genes,], 1, range), 
                                 2, function(x) sum(abs(x))))
pheatmap::pheatmap(t(diff_fits)[diff_genes,], cluster_cols = F, 
                   clustering_method = "ward.D", fontsize_row = 8, annotation_row = df)

plot(hep_fits$healthy[,"CPS1"])
plot(hep_fits$regenerating[,"CPS1"])
genes_healthy = names(qvals_b_list$healthy)[order(qvals_b_list$healthy, decreasing = F)][qvals_b_list$healthy[order(qvals_b_list$healthy, decreasing = F)]<=0.05]
genes_regen = names(qvals_b_list$regenerating)[order(qvals_b_list$regenerating, decreasing = F)][qvals_b_list$regenerating[order(qvals_b_list$regenerating, decreasing = F)]<=0.05]
gg = unique(c(genes_healthy, genes_regen))

eg = clusterProfiler::bitr(gg, fromType="SYMBOL", 
                           toType="ENTREZID", OrgDb="org.Hs.eg.db")
eg_all = clusterProfiler::bitr(names(qvals_b_list$healthy), fromType="SYMBOL", 
                           toType="ENTREZID", OrgDb="org.Hs.eg.db")

reac = ReactomePA::enrichPathway(gene=eg$ENTREZID,pvalueCutoff=0.05, 
                               readable=T, universe = eg_all$ENTREZID)

got = clusterProfiler::enrichGO(gene = eg$ENTREZID, universe = eg_all$ENTREZID, 
                                OrgDb = org.Hs.eg.db, ont = "BP", pvalueCutoff  = 0.01,
                                qvalueCutoff  = 0.05, 
                                pAdjustMethod = "BH", readable = T)

all_terms = rbind(as.data.frame(reac), as.data.frame(got))
all_terms$DB = c(rep("Reactome", nrow(as.data.frame(reac))), rep("GO Term", nrow(as.data.frame(got))))
all_terms = all_terms[all_terms$qvalue<=0.05,]
all_terms_noribo = all_terms[!grepl("RPL", all_terms$geneID) & !grepl("RPS", all_terms$geneID),]

nhealthy = c()
nregen = c()
corhealthy = c()
corregen = c()
mean_traj_h = matrix(0, nrow(all_terms_noribo), 100)
mean_traj_r = matrix(0, nrow(all_terms_noribo), 100)
cor_mean = c()
for(i in 1:nrow(all_terms_noribo)){
  genes_path = unlist(strsplit(all_terms_noribo[i,"geneID"],"/"))
  in_healthy = genes_path[genes_path %in% genes_healthy]
  in_regen = genes_path[genes_path %in% genes_regen]
  
  nhealthy = c(nhealthy, length(in_healthy))
  nregen = c(nregen, length(in_regen))
  
  if(length(in_healthy)>1){
    ch = cor(fits_b_list$healthy[,in_healthy], method = "sp")
    corhealthy = c(corhealthy, median(ch[upper.tri(ch)])) 
  } else{ 
    corhealthy = c(corhealthy,1)
  }
  if(length(in_regen)>1){
    ch = cor(fits_b_list$regen[,in_regen], method = "sp")
    corregen = c(corregen, median(ch[upper.tri(ch)])) 
  } else{ 
    corregen = c(corregen,1)
  }
  
  mean_traj_h[i,] = if(length(in_healthy)>1){ rowMeans(fits_b_list$healthy[,in_healthy])} else if(length(in_healthy)>0){fits_b_list$healthy[,in_healthy]} else {rep(0,100)}
  mean_traj_r[i,] = if(length(in_regen)>1){ rowMeans(fits_b_list$regenerating[,in_regen])} else if(length(in_regen)>0){fits_b_list$regenerating[,in_regen]} else {rep(0,100)}
  
  cor_mean = c(cor_mean, cor(mean_traj_h[i,], mean_traj_r[i,]))
}
all_terms_noribo = cbind(all_terms_noribo, nhealthy, nregen, corhealthy, corregen, cor_mean)
rownames(mean_traj_h) = rownames(mean_traj_r) = all_terms_noribo$Description


View(all_terms_noribo[all_terms_noribo$corhealthy>=0.25 & all_terms_noribo$corregen>=0.25 &
                        all_terms_noribo$nhealthy>=2 & all_terms_noribo$nregen>=2,])
i = which(all_terms_noribo$Description=="mRNA Splicing - Minor Pathway")
genes_healthy = names(qvals_b_list$healthy)[qvals_b_list$healthy<=0.05]
genes_regen = names(qvals_b_list$regenerating)[qvals_b_list$regenerating<=0.05]
genes_conds = list("healthy" = setdiff(genes_healthy, genes_regen), 
                   "regen" = setdiff(genes_regen, genes_healthy),
                   "common" = intersect(genes_healthy, genes_regen))

terms_list = list()
for(n in names(genes_conds)){
  eg = clusterProfiler::bitr(genes_conds[[n]], fromType="SYMBOL", 
                           toType="ENTREZID", OrgDb="org.Hs.eg.db")
  eg_all = clusterProfiler::bitr(names(qvals_b_list$healthy), fromType="SYMBOL", 
                             toType="ENTREZID", OrgDb="org.Hs.eg.db")
  
  reac = ReactomePA::enrichPathway(gene=eg$ENTREZID,pvalueCutoff=0.05, 
                                 readable=T, universe = eg_all$ENTREZID)
  
  got = clusterProfiler::enrichGO(gene = eg$ENTREZID, universe = eg_all$ENTREZID, 
                                  OrgDb = org.Hs.eg.db, ont = "BP", pvalueCutoff  = 0.01,
                                  qvalueCutoff  = 0.05, 
                                  pAdjustMethod = "BH", readable = T)
  
  all_terms = rbind(as.data.frame(reac), as.data.frame(got))
  all_terms$DB = c(rep("Reactome", nrow(as.data.frame(reac))), rep("GO Term", nrow(as.data.frame(got))))
  all_terms = all_terms[all_terms$qvalue<=0.05,]
  all_terms_noribo = all_terms[!grepl("RPL", all_terms$geneID) & !grepl("RPS", all_terms$geneID),]
  
  nhealthy = c()
  nregen = c()
  corhealthy = c()
  corregen = c()
  mean_traj_h = matrix(0, nrow(all_terms_noribo), 100)
  mean_traj_r = matrix(0, nrow(all_terms_noribo), 100)
  cor_mean = c()
  for(i in 1:nrow(all_terms_noribo)){
    genes_path = unlist(strsplit(all_terms_noribo[i,"geneID"],"/"))
    in_healthy = genes_path[genes_path %in% genes_healthy]
    in_regen = genes_path[genes_path %in% genes_regen]
    
    nhealthy = c(nhealthy, length(in_healthy))
    nregen = c(nregen, length(in_regen))
    
    if(length(in_healthy)>1){
      ch = cor(fits_b_list$healthy[,in_healthy], method = "sp")
      corhealthy = c(corhealthy, median(ch[upper.tri(ch)])) 
    } else{ 
      corhealthy = c(corhealthy,0)
    }
    if(length(in_regen)>1){
      ch = cor(fits_b_list$regen[,in_regen], method = "sp")
      corregen = c(corregen, median(ch[upper.tri(ch)])) 
    } else{ 
      corregen = c(corregen,0)
    }
    
    mean_traj_h[i,] = if(length(in_healthy)>1){ rowMeans(fits_b_list$healthy[,in_healthy])} else if(length(in_healthy)>0){fits_b_list$healthy[,in_healthy]} else {rep(0,100)}
    mean_traj_r[i,] = if(length(in_regen)>1){ rowMeans(fits_b_list$regenerating[,in_regen])} else if(length(in_regen)>0){fits_b_list$regenerating[,in_regen]} else {rep(0,100)}
    
    cor_mean = c(cor_mean, cor(mean_traj_h[i,], mean_traj_r[i,]))
  }
  all_terms_noribo = cbind(all_terms_noribo, nhealthy, nregen, corhealthy, corregen, cor_mean)
  rownames(mean_traj_h) = rownames(mean_traj_r) = all_terms_noribo$Description
  
  terms_list[[n]] = all_terms_noribo
}
cl = hclust(dist(t(diff_fits)), method = "ward.D")
ct = cutree(cl, 1)

term_list = list()
for(i in unique(ct)){
  fsel = function(x) return(x<0.25 & names(genes) %in% names(ct)[ct==i])
  genes = hep_cor_fit_list$healthy_regenerating$X..i..
  names(genes) = hep_cor_fit_list$healthy_regenerating[,1]
  genes = genes[!is.na(genes)]
  
  eg = clusterProfiler::bitr(names(genes[fsel(genes)]), fromType="SYMBOL", 
                             toType="ENTREZID", OrgDb="org.Hs.eg.db")
  eg_all = clusterProfiler::bitr(names(genes), fromType="SYMBOL", 
                             toType="ENTREZID", OrgDb="org.Hs.eg.db")
  
  x <- ReactomePA::enrichPathway(gene=eg$ENTREZID,pvalueCutoff=0.05, 
                                 readable=T, universe = eg_all$ENTREZID)
  
  term_list[[paste0("reac_",i)]] = x
  
  ego3 <- clusterProfiler::enrichGO(gene = eg$ENTREZID, universe = eg_all$ENTREZID, 
                                 OrgDb = org.Hs.eg.db, ont = "BP", pvalueCutoff  = 0.01,
                                 qvalueCutoff  = 0.05, 
                                 pAdjustMethod = "BH", readable = T)
  
  term_list[[paste0("go_",i)]] = ego3
}
genes = hep_cor_fit_list$healthy_regenerating$X..i..
names(genes) = hep_cor_fit_list$healthy_regenerating[,1]
genes = genes[!is.na(genes)]

# GO enrichment
fsel = function(x) return(x<0.25 & names(genes) %in% gg)

sampleGOdata <- new("topGOdata",
                    description = "test", ontology = "BP",
                    allGenes = genes, geneSel = fsel,
                    nodeSize = 5, annot = annFUN.org, 
                    mapping = "org.Hs.eg.db", ID = "symbol")

resultKS.elim <- runTest(sampleGOdata, algorithm = "elim", 
                         statistic = "fisher")

resGO = GenTable(object = sampleGOdata, elimKS = resultKS.elim,
                 topNodes = resultKS.elim@geneData[4], numChar = 1000)
resGO$elimKS = score(resultKS.elim)[resGO$GO.ID]


# get genes
relevant_genes = sampleGOdata@allGenes[sampleGOdata@geneSelectionFun(sampleGOdata@allScores)]
goGenes = t(data.frame(lapply(genesInTerm(sampleGOdata), function(x) paste(x[x %in% relevant_genes], collapse = ", "))))
rownames(goGenes) = gsub(".", ":", rownames(goGenes), fixed = T)
colnames(goGenes) = "genes"
resGO = merge(resGO, goGenes, by.x = 1, by.y = 0, all.x = T)
subresGO = resGO[!(grepl("RPL", resGO$genes) & grepl("RPS", resGO$genes)),]
sig_resGO = resGO[resGO$elimKS<=0.05,]
hprof_go = list()
rprof_go = list()
for(l in 1:nrow(sig_resGO)){
  genes_go = unlist(strsplit(as.character(sig_resGO[l,"genes"]), ", "))
  
  hprof_go[[sig_resGO[l,2]]] = t(hep_fits$healthy[,genes_go])
  rprof_go[[sig_resGO[l,2]]] = t(hep_fits$regenerating[,genes_go])
}
hmax = unlist(lapply(hprof_go, function(x) which.max(colSums(x))))
rmax = unlist(lapply(rprof_go, function(x) which.max(colSums(x))))
names(hmax) = names(rmax) = sig_resGO$Term
sig_resGO = sig_resGO[order(sig_resGO$elimKS, decreasing = F),]
go_short_list = list()
for(i in 1:nrow(sig_resGO)){
  g = unlist(strsplit(as.character(sig_resGO[i,"genes"]), ", "))
  if(!any(g %in% unlist(go_short_list)) & length(g)>1){
    go_short_list[[sig_resGO[i,2]]] = g
  }
}

res = matrix(0, 1, 100)
for(n in names(go_short_list)){
  if(length(go_short_list[[n]])>1){
    cl = hclust(dist(t(diff_fits[,go_short_list[[n]]])), method = "ward.D")
    res = rbind(res, t(diff_fits[,go_short_list[[n]]])[cl$order,])
  }
}
res = res[-1,]


pheatmap::pheatmap(res, cluster_cols = F, cluster_rows = F, fontsize_row = 8, gaps_row = cumsum(unlist(lapply(go_short_list, length))))
sig_resGO = sig_resGO[order(sig_resGO$elimKS, decreasing = F),]
go_med = matrix(0, 1, 100)
for(i in 1:nrow(sig_resGO)){
  g = unlist(strsplit(as.character(sig_resGO[i,"genes"]), ", "))
  if(length(g)>1){
    go_med = rbind(go_med, apply(t(diff_fits[,g]), 2, median))
    rownames(go_med)[nrow(go_med)] = sig_resGO[i,2]
  }
}
go_med = go_med[-1,]
eg = clusterProfiler::bitr(names(genes[fsel(genes)]), fromType="SYMBOL", 
                           toType="ENTREZID", OrgDb="org.Hs.eg.db")
eg_all = clusterProfiler::bitr(names(genes), fromType="SYMBOL", 
                           toType="ENTREZID", OrgDb="org.Hs.eg.db")

x <- as.data.frame(ReactomePA::enrichPathway(gene=eg$ENTREZID,pvalueCutoff=0.05, 
                                             readable=T, universe = eg_all$ENTREZID))
head(as.data.frame(x))

go_med = matrix(0, 1, 100)
for(i in 1:nrow(x)){
  g = unlist(strsplit(as.character(x[i,"geneID"]), "/"))
  if(length(g)>1){
    go_med = rbind(go_med, apply(t(diff_fits[,g]), 2, median))
    rownames(go_med)[nrow(go_med)] = x[i,2]
  }
}
go_med = go_med[-1,]

genes_reac = strsplit(x$geneID, "/")
names(genes_reac) = x$Description
genes_reac = reshape2::melt(genes_reac)
dat = genes_reac[!duplicated(genes_reac$value),]
dat = data.frame(row.names = dat[,1], "Reac" = dat[,2])
pheatmap::pheatmap(t(diff_fits)[diff_genes,], cluster_cols = F, 
                   clustering_method = "ward.D", fontsize_row = 8, annotation_row = dat)

hprof_re = list()
rprof_re = list()
for(l in 1:nrow(x)){
  genes_go = unlist(strsplit(as.character(x[l,"geneID"]), "/"))
  
  hprof_re[[x[l,2]]] = t(hep_fits$healthy[,genes_go])
  rprof_re[[x[l,2]]] = t(hep_fits$regenerating[,genes_go])
}


hmet = ggplot(reshape2::melt(t(apply(hprof_re$`Metallothioneins bind metals`, 1, scale))), 
              aes(x = Var2, y = value, group = Var1, colour = Var1))+
  geom_line()+labs(title = "Healthy - Metallothioneins bind metals")
rmet = ggplot(reshape2::melt(t(apply(rprof_re$`Metallothioneins bind metals`, 1, scale))), 
              aes(x = Var2, y = value, group = Var1, colour = Var1))+
  geom_line()+labs(title = "Regen - Metallothioneins bind metals")
rlip = ggplot(reshape2::melt(t(apply(rprof_re$`Plasma lipoprotein remodeling`, 1, scale))), 
              aes(x = Var2, y = value, group = Var1, colour = Var1))+
  geom_line()+labs(title = "Regen - Plasma lipoprotein remodeling")
hlip = ggplot(reshape2::melt(t(apply(hprof_re$`Plasma lipoprotein remodeling`, 1, scale))), 
              aes(x = Var2, y = value, group = Var1, colour = Var1))+
  geom_line()+labs(title = "Healthy - Plasma lipoprotein remodeling")
cowplot::plot_grid(hmet, rmet, hlip, rlip, ncol = 2, row = 2, align = "hv")









ego3 <- clusterProfiler::enrichGO(gene = eg$ENTREZID, universe = eg_all$ENTREZID, 
                               OrgDb = org.Hs.eg.db, ont = "BP", pvalueCutoff  = 0.01,
                               qvalueCutoff  = 0.05, 
                               pAdjustMethod = "BH", readable = T)


genes_ent = merge(eg_all, genes, by.x = 1, by.y = 0)
tmp = genes_ent[,2]
genes_ent = genes_ent[,3]
names(genes_ent) = tmp
ego4 <- clusterProfiler::gseGO(geneList = genes_ent[order(genes_ent, decreasing = T)], 
                               OrgDb = org.Hs.eg.db, ont = "BP", nPerm = 1000, minGSSize = 100,
                               maxGSSize = 500, pvalueCutoff = 0.05, verbose = FALSE)
plot_genes_fits = cbind(hep_fits$healthy[,c("SAA1", "HAMP", "CYP3A4", "GLUL", "CPS1", "ARG1")],
                        hep_fits$embolised[,c("SAA1", "HAMP", "CYP3A4", "GLUL", "CPS1", "ARG1")],
                        hep_fits$regenerating[,c("SAA1", "HAMP", "CYP3A4", "GLUL", "CPS1", "ARG1")])
plot_genes_fits = apply(plot_genes_fits, 2, c)

colnames(plot_genes_fits) = paste0(colnames(plot_genes_fits), 
                                   rep(c("_healthy", "_embolised", "_regenerating"), each = 6))
plot_genes_fits = reshape2::melt(plot_genes_fits)

plot_genes_fits$cond = unlist(lapply(strsplit(as.character(plot_genes_fits$Var2), "_"), function(x) x[2]))

plot_genes_fits$cond = factor(plot_genes_fits$cond, levels = c("healthy", "embolised", "regenerating"))

plot_genes_fits$genes = unlist(lapply(strsplit(as.character(plot_genes_fits$Var2), "_"), function(x) x[1]))

ggplot(plot_genes_fits, aes(x = Var1, y = value, colour = genes))+facet_wrap(~cond)+geom_line()
all_path = terms_groups$all[rpfilter(terms_groups$all),]
all_path = all_path[order(all_path$qvalue, decreasing = F),]

tab_go = reshape2::melt(lapply(df_cor$gene, function(g) all_path[which(grepl(g, all_path$geneID) & all_path$DB=="GO Term")[1],c("Description", "DB", "qvalue")][1,]))
tab_go$gene = df_cor$gene
tab_go = tab_go[,c(6,1,2,4)]
tab_go$de = apply(df_cor[,c(1,3,4,5)], 1, function(x) colnames(df_cor[,c(3,4,5)])[which(as.logical(x[2:4]))])
tab_go$de[nchar(tab_go$de)>10] = NA
tab_go$de = unlist(tab_go$de)
tab_go$corr = df_cor$corr
tab_re = reshape2::melt(lapply(df_cor$gene, function(g) all_path[which(grepl(g, all_path$geneID) & all_path$DB=="Reactome")[1],c("Description", "DB", "qvalue")][1,]))
tab_re$gene = df_cor$gene
tab_re = tab_re[,c(6,1,2,4)]
tab_re$de = apply(df_cor[,c(1,3,4,5)], 1, function(x) colnames(df_cor[,c(3,4,5)])[which(as.logical(x[2:4]))])
tab_re$de[nchar(tab_re$de)>10] = NA
tab_re$de = unlist(tab_re$de)
tab_re$corr = df_cor$corr

tab_all = rbind(tab_go, tab_re)
tab_all = tab_all[complete.cases(tab_all),]

xxx = table(tab_all$Description, tab_all$de)
xxx = xxx[rowSums(xxx)>2,]
xxx = xxx/rowSums(xxx)
View(apply(xxx, 1, function(x) ifelse(any(x>0.5), colnames(xxx)[x>0.5], NA)))

aaa = cbind(tapply(tab_all$corr, tab_all$Description, function(x) sum(x>=0.25)/length(x)), 
            tapply(tab_all$corr, tab_all$Description, function(x) length(x)),
            tapply(tab_all$de, tab_all$Description, function(x) names(table(x))[which.max(table(x))]))

Calculate distances for all significant genes

for(cond in names(qvals_list)){
  gene_fit_p = qvals_list[[cond]]
  gene_fit_vals = fits_list[[cond]]
  gene_fit_vals_filt = t(gene_fit_vals[,names(gene_fit_p)[gene_fit_p<=0.05]])
  scale_fit = t(apply(gene_fit_vals_filt, 1, scale))
  
  # dist
  if(file.exists(paste0("results/zonation_cond/dtw_distance_end_", cond, "_sigGenes.RDS"))){
    next
  } else{
    dd = dtw::dtwDist(scale_fit) # TAKES 4.5 HOURS
    saveRDS(dd, paste0("results/zonation_cond/dtw_distance_end_", cond, "_sigGenes.RDS"))
  }
}

Cluster genes (per condition)

top_n = 1000
for(cond in names(qvals_list)){
  print(cond)
  gene_fit_p = qvals_list[[cond]]
  gene_fit_vals = fits_list[[cond]]
  
  dd = readRDS(paste0("results/zonation_cond/dtw_distance_end_", cond, "_sigGenes.RDS"))
  
  gene_fit_vals_filt = t(gene_fit_vals[,names(gene_fit_p)[gene_fit_p<=0.05]])
  scale_fit = t(apply(gene_fit_vals_filt, 1, scale))
  
  # select top genes (0 if using all)
  topgenes = if(top_n==0) names(gene_fit_p) else names(head(gene_fit_p[order(gene_fit_p,
                                                                             decreasing = F)],
                                                            top_n))
  
  clust_genes = hclust(as.dist(dd[topgenes,topgenes]), method = "ward.D2")
  
  cls = cutree(clust_genes, 4)
  #ape::plot.phylo(ape::as.phylo(clust_genes), tip.color = rainbow(10)[cls])
  plt_list = list()
  for(i in unique(cls)){
    sub_df = scale_fit[names(cls)[cls==i],]
    plot_df = reshape2::melt(sub_df)
    plt_list[[paste0("gene_",i)]] = ggplot(plot_df, aes(x = Var2, y = value, group = Var1))+
      geom_hline(yintercept = 0, linetype = "dashed")+
      geom_line(alpha = 0.1, colour = "grey69")+
      stat_summary(aes(y = value, group=1), fun=mean, colour="red", geom="line",group=1)+
      labs(title = paste0("gene_",i))+
      theme_bw()
  }
  
  pdf(paste0("results/zonation_cond/end_gene_expression_profile_", cond, ".pdf"),
      height = 4.5, width = 4.4)
  print(cowplot::plot_grid(plotlist = plt_list, nrow = 2, ncol = 2, align = "hv"))
  dev.off()
  
  df_pval_cl = merge(data.frame(gene_fit_p), data.frame(cls), by = 0, all = T)
  colnames(df_pval_cl) = c("gene", "pval", "cluster")
  
  write.csv(df_pval_cl, file = paste0("results/zonation_cond/end_pval_clust_", cond, ".csv"),
            col.names = T, row.names = F, quote = F)

  write.csv(gene_fit_vals, 
            file = paste0("results/zonation_cond/end_gene_fits_", cond, ".csv"),
            col.names = T, row.names = F, quote = F)
}

Calculate distances for all significant genes

for(cond in names(qvals_list)){
  gene_fit_p = qvals_list[[cond]]
  gene_fit_vals = fits_list[[cond]]
  gene_fit_vals_filt = t(gene_fit_vals[,names(gene_fit_p)[gene_fit_p<=0.05]])
  scale_fit = t(apply(gene_fit_vals_filt, 1, scale))
  
  # dist
  if(file.exists(paste0("results/zonation_cond/dtw_distance_", cond, "_sigGenes.RDS"))){
    next
  } else{
    dd = dtw::dtwDist(scale_fit) # TAKES 4.5 HOURS
    saveRDS(dd, paste0("results/zonation_cond/dtw_distance_", cond, "_sigGenes.RDS"))
  }
}

Cluster genes (per condition)

top_n = 1000
for(cond in names(qvals_list)){
  print(cond)
  gene_fit_p = qvals_list[[cond]]
  gene_fit_vals = fits_list[[cond]]
  
  dd = readRDS(paste0("results/zonation_cond/dtw_distance_", cond, "_sigGenes.RDS"))
  
  gene_fit_vals_filt = t(gene_fit_vals[,names(gene_fit_p)[gene_fit_p<=0.05]])
  scale_fit = t(apply(gene_fit_vals_filt, 1, scale))
  
  # select top genes (0 if using all)
  topgenes = if(top_n==0) names(gene_fit_p) else names(head(gene_fit_p[order(gene_fit_p,
                                                                             decreasing = F)],
                                                            top_n))
  
  clust_genes = hclust(as.dist(dd[topgenes,topgenes]), method = "ward.D2")
  
  cls = cutree(clust_genes, 4)
  #ape::plot.phylo(ape::as.phylo(clust_genes), tip.color = rainbow(10)[cls])
  plt_list = list()
  for(i in unique(cls)){
    sub_df = scale_fit[names(cls)[cls==i],]
    plot_df = reshape2::melt(sub_df)
    plt_list[[paste0("gene_",i)]] = ggplot(plot_df, aes(x = Var2, y = value, group = Var1))+
      geom_hline(yintercept = 0, linetype = "dashed")+
      geom_line(alpha = 0.1, colour = "grey69")+
      stat_summary(aes(y = value, group=1), fun=mean, colour="red", geom="line",group=1)+
      labs(title = paste0("gene_",i))+
      theme_bw()
  }
  
  pdf(paste0("results/zonation_cond/hep_gene_expression_profile_", cond, ".pdf"),
      height = 4.5, width = 4.4)
  print(cowplot::plot_grid(plotlist = plt_list, nrow = 2, ncol = 2, align = "hv"))
  dev.off()
  
  df_pval_cl = merge(data.frame(gene_fit_p), data.frame(cls), by = 0, all = T)
  colnames(df_pval_cl) = c("gene", "pval", "cluster")
  
  write.csv(df_pval_cl, file = paste0("results/zonation_cond/hep_pval_clust_", cond, ".csv"),
            col.names = T, row.names = F, quote = F)

  write.csv(gene_fit_vals, file = paste0("results/zonation_cond/hep_gene_fits_", cond, ".csv"),
            col.names = T, row.names = F, quote = F)
}
LS0tCnRpdGxlOiAiWm9uYXRpb24gYW5hbHlzaXMgVjIiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCgoKIyBHZW5lcmFsIFNldHVwClNldHVwIGNodW5rCgpgYGB7ciwgc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGggPSA4KQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9IG5vcm1hbGl6ZVBhdGgoIi4uIikpCmtuaXRyOjpvcHRzX2tuaXQkZ2V0KCJyb290LmRpciIpCmBgYAoKU2V0dXAgcmV0aWN1bGF0ZQoKYGBge3J9CmxpYnJhcnkocmV0aWN1bGF0ZSkKa25pdHI6OmtuaXRfZW5naW5lcyRzZXQocHl0aG9uID0gcmV0aWN1bGF0ZTo6ZW5nX3B5dGhvbikKcHlfYXZhaWxhYmxlKGluaXRpYWxpemUgPSBGQUxTRSkKdXNlX3B5dGhvbihTeXMud2hpY2goInB5dGhvbiIpKQpweV9jb25maWcoKQpgYGAKCkxvYWQgbGlicmFyaWVzCgpgYGB7cn0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JpZGdlcykKbGlicmFyeShkcGx5cikKbGlicmFyeShkZXN0aW55KQpsaWJyYXJ5KGZkcnRvb2wpCmxpYnJhcnkoZ2FtKQpsaWJyYXJ5KGdhbWNsYXNzKQpsaWJyYXJ5KGR0dykKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShSZWFjdG9tZVBBKQpgYGAKCkZ1bmN0aW9uIGZvciBjcm9zcy12YWxpZGF0aW9uCgpgYGB7cn0KQ1ZnYW0gPSBmdW5jdGlvbiAoZm9ybXVsYSwgZGF0YSwgbmZvbGQgPSAxMCwgZGVidWcubGV2ZWwgPSAwLCBtZXRob2QgPSAiZ2xtLmZpdCIsIAogICAgcHJpbnRpdCA9IFRSVUUsIGN2cGFydHMgPSBOVUxMLCBnYW1tYSA9IDEsIHNlZWQgPSAyOSl7CiAgIyBtb2RpZmllZCBmcm9tIHRoZSBgZ2FtY2xhc3NgIHBhY2thZ2UgdG8gdXNlIGdhbTo6Z2FtCiAgICBpZiAoaXMubnVsbChjdnBhcnRzKSkgewogICAgICAgIHNldC5zZWVkKHNlZWQpCiAgICAgICAgY3ZwYXJ0cyA8LSBzYW1wbGUoMTpuZm9sZCwgbnJvdyhkYXRhKSwgcmVwbGFjZSA9IFRSVUUpCiAgICB9CiAgICBmb2xkcyA8LSB1bmlxdWUoY3ZwYXJ0cykKICAgIGtoYXQgPC0gaGF0IDwtIG51bWVyaWMobnJvdyhkYXRhKSkKICAgIHNjYWxlLmdhbSA8LSBzdW1tYXJ5KGdhbTo6Z2FtKGZvcm11bGEsIGRhdGEgPSBkYXRhLCBtZXRob2QgPSBtZXRob2QpKSRzY2FsZQogICAgZm9yIChpIGluIGZvbGRzKSB7CiAgICAgICAgdHJhaW5yb3dzIDwtIGN2cGFydHMgIT0gaQogICAgICAgIHRlc3Ryb3dzIDwtIGN2cGFydHMgPT0gaQogICAgICAgIGVsZXYuZ2FtIDwtIGdhbTo6Z2FtKGZvcm11bGEsIGRhdGEgPSBkYXRhW3RyYWlucm93cywgXSwgbWV0aG9kID0gbWV0aG9kLCAKICAgICAgICAgICAgZ2FtbWEgPSBnYW1tYSkKICAgICAgICBoYXRbdGVzdHJvd3NdIDwtIHByZWRpY3QoZWxldi5nYW0sIG5ld2RhdGEgPSBkYXRhW3Rlc3Ryb3dzLCAKICAgICAgICAgICAgXSwgc2VsZWN0ID0gVFJVRSkKICAgICAgICByZXMgPC0gcmVzaWR1YWxzKGVsZXYuZ2FtKQogICAgfQogICAgeSA8LSBldmFsKGZvcm11bGFbWzJdXSwgZW52aXIgPSBhcy5kYXRhLmZyYW1lKGRhdGEpKQogICAgcmVzIDwtIHkgLSBoYXQKICAgIGN2c2NhbGUgPC0gc3VtKHJlc14yKS9sZW5ndGgocmVzKQogICAgcHJudHZlYyA8LSBjKEdBTXNjYWxlID0gc2NhbGUuZ2FtLCBgQ1YtbXNlLUdBTSBgID0gY3ZzY2FsZSkKICAgIGlmIChwcmludGl0KSAKICAgICAgICBwcmludChyb3VuZChwcm50dmVjLCA0KSkKICAgIGludmlzaWJsZShsaXN0KGZpdHRlZCA9IGhhdCwgcmVzaWQgPSByZXMsIGN2c2NhbGUgPSBjdnNjYWxlLCAKICAgICAgICBzY2FsZS5nYW0gPSBzY2FsZS5nYW0pKQp9CmBgYAoKCgojIExvYWQgZGF0YQpMb2FkIGRhdGEgKGZyb20gYWxsIGNlbGxzKQoKYGBge3J9CmFsbGNlbGxzX2NzcyA9IHJlYWRSRFMoZmlsZSA9ICJkYXRhL3Byb2Nlc3NlZC9hbGxjZWxsc19jc3MuUkRTIikKYGBgCgoKCiMgSGVwYXRvY3l0ZXMKU3Vic2V0IGFuZCBwcm9jZXNzIGVhY2ggY29uZGl0aW9uCgpgYGB7cn0KaGVwX2NlbGxzID0gbGlzdCgpCmZvcihjb25kIGluIHVuaXF1ZShhbGxjZWxsc19jc3NAbWV0YS5kYXRhJENvbmRpdGlvbikpewogIGNhdChjb25kKQogIGhlcF9jZWxsc1tbY29uZF1dID0gYWxsY2VsbHNfY3NzWyxhbGxjZWxsc19jc3NAbWV0YS5kYXRhJGFsbGNlbGxzX21ham9yPT0iSGVwYXRvY3l0ZXMiICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsbGNlbGxzX2Nzc0BtZXRhLmRhdGEkQ29uZGl0aW9uPT1jb25kXQogIHByaW50KGRpbShoZXBfY2VsbHNbW2NvbmRdXSkpCiAgaGVwX2NlbGxzW1tjb25kXV0gPSBzdXBwcmVzc1dhcm5pbmdzKFNDVHJhbnNmb3JtKGhlcF9jZWxsc1tbY29uZF1dLCBkby5jb3JyZWN0LnVtaSA9IFQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnMudG8ucmVncmVzcz1jKCJ1bmlxdWVfbmFtZSIsIm5Db3VudF9STkEiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMucnYudGggPSAxLCBzZWVkLnVzZSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybi5vbmx5LnZhci5nZW5lcyA9IEYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5uID0gTlVMTCkpCiAgaGVwX2NlbGxzW1tjb25kXV0gPSBSdW5QQ0EoaGVwX2NlbGxzW1tjb25kXV0sIHZlcmJvc2UgPSBGKQogIGhlcF9jZWxsc1tbY29uZF1dID0gUnVuVU1BUChoZXBfY2VsbHNbW2NvbmRdXSwgZGltcyA9IDE6MjAsIHZlcmJvc2UgPSBGKQp9CmBgYAoKCiMjIEhlYWx0aHkgYW5hbHlzaXMKRm9yIGhlYWx0aHkgY2VsbHMgKHdoaWNoIHdpbGwgc2VydmUgYXMgcmVmZXJlbmNlKSwgZG8gYWRkaXRpb25hbCBmaWx0ZXJpbmcgYW5kIHJlcHJvY2Vzc2luZwoKYGBge3J9CkRpbVBsb3QoaGVwX2NlbGxzJGhlYWx0aHksIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAidW5pcXVlX25hbWUiKQpvdXRjZWxscyA9IChoZXBfY2VsbHMkaGVhbHRoeUByZWR1Y3Rpb25zJHVtYXBAY2VsbC5lbWJlZGRpbmdzWywxXT4oMSkgJgogICAgICAgICAgICAgIGhlcF9jZWxscyRoZWFsdGh5QHJlZHVjdGlvbnMkdW1hcEBjZWxsLmVtYmVkZGluZ3NbLDJdPjUpCmhlcF9jZWxscyRoZWFsdGh5ID0gaGVwX2NlbGxzJGhlYWx0aHlbLCFvdXRjZWxsc10KCmhlcF9jZWxscyRoZWFsdGh5ID0gc3VwcHJlc3NXYXJuaW5ncyhTQ1RyYW5zZm9ybShoZXBfY2VsbHMkaGVhbHRoeSwgZG8uY29ycmVjdC51bWkgPSBULCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFycy50by5yZWdyZXNzID0gYygidW5pcXVlX25hbWUiLCJuQ291bnRfUk5BIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5ydi50aCA9IDEsIHNlZWQudXNlID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybi5vbmx5LnZhci5nZW5lcyA9IEYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMubiA9IE5VTEwpKQpoZXBfY2VsbHMkaGVhbHRoeSA9IFJ1blBDQShoZXBfY2VsbHMkaGVhbHRoeSwgdmVyYm9zZSA9IEYpCmhlcF9jZWxscyRoZWFsdGh5ID0gUnVuVU1BUChoZXBfY2VsbHMkaGVhbHRoeSwgZGltcyA9IDE6MTUsIHZlcmJvc2UgPSBGKQpEaW1QbG90KGhlcF9jZWxscyRoZWFsdGh5LCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gInVuaXF1ZV9uYW1lIikKCmhlcF9jZWxscyRoZWFsdGh5ID0gRmluZE5laWdoYm9ycyhoZXBfY2VsbHMkaGVhbHRoeSwgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOjE1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9yY2UucmVjYWxjID0gVCwgZ3JhcGgubmFtZSA9ICJoZXAiKQpoZXBfY2VsbHMkaGVhbHRoeSA9IEZpbmRDbHVzdGVycyhoZXBfY2VsbHMkaGVhbHRoeSwgZ3JhcGgubmFtZSA9ICJoZXAiLCBhbGdvcml0aG0gPSA0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x1dGlvbiA9IHNlcSgwLjEsMC44LDAuMSksIHZlcmJvc2UgPSBGKQpEaW1QbG90KGhlcF9jZWxscyRoZWFsdGh5LCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImhlcF9yZXMuMC4zIikKaGVwX2NlbGxzJGhlYWx0aHkgPSBTZXRJZGVudChoZXBfY2VsbHMkaGVhbHRoeSwgdmFsdWUgPSAiaGVwX3Jlcy4wLjYiKQpta19oZXBfaCA9IEZpbmRBbGxNYXJrZXJzKGhlcF9jZWxscyRoZWFsdGh5LCBhc3NheSA9ICJTQ1QiKQoKc2F2ZVJEUyhoZXBfY2VsbHMkaGVhbHRoeSwgZmlsZSA9ICJyZXN1bHRzL3pvbmF0aW9uX2hlYWx0aHkvaGVwX2hlYWx0aHlfc3JhdC5SRFMiKQpgYGAKClNhdmUgaGVwYXRvY3l0ZXMKCmBgYHtyfQpzYXZlUkRTKGhlcF9jZWxscywgZmlsZT0icmVzdWx0cy96b25hdGlvbl9jb25kL2hlcF9jZWxscy5SRFMiKQpgYGAKClBsb3QgZXhwcmVzc2lvbiBvZiB6b25hdGlvbiBtYXJrZXJzCgpgYGB7cn0KZ2ZyZXNoID0gYygiQ1lQMUEyIiwgIkNZUDJFMSIsICJHTFVMIiwgIkNZUDNBNCIsICJHMFMyIiwgIkFQT0MxIiwgIlVHVDJCMTciLCAiQkNIRSIsICMgY2VudHJhbCAKICAgICAgICAgICAiU0FBMSIsICJIQU1QIiwgIlNBQTIiLCAiQ1BTMSIsICJDMyIsICJDMVIiLCAiTVQtTkQ0IiwgIk1ULUNPMiIpICMgcG9ydGFsCgpwbmcoInJlc3VsdHMvem9uYXRpb25faGVhbHRoeS9oZWFsdGh5X2hlcGF0b2N5dGVfem9uYXRpb25fbWFya2Vyc192aW9saW4ucG5nIiwgCiAgICBoZWlnaHQgPSAxNDAwLCB3aWR0aCA9IDE0MDApClZsblBsb3QoaGVwX2NlbGxzJGhlYWx0aHksIGZlYXR1cmVzID0gZ2ZyZXNoLCBncm91cC5ieSA9ICJuYW1lc19jbHVzdGVycyIsIAogICAgICAgIHB0LnNpemUgPSAwLCBzb3J0ID0gImluY3JlYXNpbmciKQpkZXYub2ZmKCkKcG5nKCJyZXN1bHRzL3pvbmF0aW9uX2hlYWx0aHkvaGVhbHRoeV9oZXBhdG9jeXRlX3pvbmF0aW9uX21hcmtlcnNfdW1hcC5wbmciLCAKICAgIGhlaWdodCA9IDE0MDAsIHdpZHRoID0gMTQwMCkKRmVhdHVyZVBsb3QoaGVwX2NlbGxzJGhlYWx0aHksIGZlYXR1cmVzID0gZ2ZyZXNoLCBwdC5zaXplID0gMC42KQpkZXYub2ZmKCkKYGBgCgpPYnRhaW4gRGlmZnVzaW9uIG1hcHMgcHJvamVjdGlvbgoKYGBge3J9CnNldC5zZWVkKDEpCmRtID0gRGlmZnVzaW9uTWFwKGhlcF9jZWxscyRoZWFsdGh5QHJlZHVjdGlvbnMkcGNhQGNlbGwuZW1iZWRkaW5nc1ssMTo0XSwgCiAgICAgICAgICAgICAgICAgIHJvdGF0ZSA9IFQsIG5fZWlncyA9IDUpCmRwdCA9IERQVChkbSkKcGxvdChkcHQpCmBgYAoKQ2hlY2sgZXhwcmVzc2lvbiBvZiBzb21lIGdlbmVzCgpgYGB7cn0KZHB0X2ZsYXQgPC0gYnJhbmNoX2RpdmlkZShkcHQsIGludGVnZXIoMEwpKQpyb290ID0gbWluKGRwdF9mbGF0QGJyYW5jaFssIDFdLCBuYS5ybSA9IFRSVUUpCmRwdF92YWxzID0gZGVzdGlueTo6OmRwdF9mb3JfYnJhbmNoKGRwdF9mbGF0LCByb290KQpkZl9kYyA9IGNiaW5kKGhlcF9jZWxscyRoZWFsdGh5QG1ldGEuZGF0YSwgCiAgICAgICAgICAgICAgZGF0YS5mcmFtZSgiREMxIiA9IGRwdEBkbSREQzEsICJEQzIiID0gZHB0QGRtJERDMiwgIkRQVCIgPSBkcHRfdmFscyksCiAgICAgICAgICAgICAgTWF0cml4Ojp0KGhlcF9jZWxscyRoZWFsdGh5QGFzc2F5cyRTQ1RAZGF0YVtnZnJlc2gsXSkpCmNvbG5hbWVzKGRmX2RjKSA9IGdzdWIoIi0iLCAiLiIsIGNvbG5hbWVzKGRmX2RjKSwgZml4ZWQgPSBUKQoKcGx0X2xpc3QgPSBsaXN0KCkKZm9yKG4gaW4gYyhnc3ViKCItIiwgIi4iLCBnZnJlc2gsIGZpeGVkID0gVCksICJuYW1lc19jbHVzdGVycyIsICJ1bmlxdWVfbmFtZSIpKXsKICBwbHRfbGlzdFtbbl1dID0gZ2dwbG90KGRmX2RjLCBhZXNfc3RyaW5nKHggPSAiREMxIiwgeSA9ICJEQzIiLCBjb2xvdXIgPSBuKSkrCiAgICBnZW9tX3BvaW50KCkrCiAgICB0aGVtZV9jbGFzc2ljKCkKfQoKcG5nKCJyZXN1bHRzL3pvbmF0aW9uX2hlYWx0aHkvaGVhbHRoeV9oZXBhdG9jeXRlX3pvbmF0aW9uX21hcmtlcnNfZGMucG5nIiwgCiAgICBoZWlnaHQgPSA5MDAsIHdpZHRoID0gMjgwMCkKY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gcGx0X2xpc3QsIG5yb3cgPSAzLCBuY29sID0gNiwgYWxpZ24gPSAiaHYiKQpkZXYub2ZmKCkKYGBgCgpXZSdsbCBiZSB1c2luZyBEQzEgYXMgdGhlIHBzZXVkb3RpbWUuIFRoaXMgd2FzIHRoZSBiZXN0IGFwcHJveGltYXRpb24gcG9zc2libGUgdG8gdGhlIHpvbmF0aW9uIGdyYWRpZW50IGJhc2VkIG9uIGRpZmZ1c2lvbiBtYXBzCgpgYGB7cn0KcGRmKCJyZXN1bHRzL3pvbmF0aW9uX2hlYWx0aHkvaGVwX2Rpc3RyaWJ1dGlvbnNfZGMxLnBkZiIsIHVzZURpbmdiYXRzID0gRiwgCiAgICBoZWlnaHQgPSA0LCB3aWR0aCA9IDkpCnBsdDEgPSBnZ3Bsb3QoZGZfZGMsIGFlcyh4ID0gREMxLCB5ID0gbmFtZXNfY2x1c3RlcnMsIAogICAgICAgICAgICAgICAgICBncm91cCA9IG5hbWVzX2NsdXN0ZXJzLCBmaWxsID0gbmFtZXNfY2x1c3RlcnMpKSsKICBnZW9tX2RlbnNpdHlfcmlkZ2VzMihhbHBoYSA9IDAuNCkrCiAgdGhlbWVfYncoKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpwbHQyID0gZ2dwbG90KGRmX2RjLCBhZXMoeCA9IERDMSwgeSA9IHVuaXF1ZV9uYW1lLCAKICAgICAgICAgICAgICAgICAgZ3JvdXAgPSB1bmlxdWVfbmFtZSwgZmlsbCA9IHVuaXF1ZV9uYW1lKSkrCiAgZ2VvbV9kZW5zaXR5X3JpZGdlczIoYWxwaGEgPSAwLjQpKwogIHRoZW1lX2J3KCkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKY293cGxvdDo6cGxvdF9ncmlkKHBsdDEsIHBsdDIsIG5jb2wgPSAyLCBhbGlnbiA9ICJoIikKZGV2Lm9mZigpCmBgYAoKRmluZCBnZW5lcyB2YXJ5aW5nIGFsb25nIHRoZSBwc2V1ZG90aW1lCgpgYGB7cn0KIyB3aWxsIG9ubHkgdXNlIHZhcmlhYmxlIGdlbmVzCmh2ZyA9IGhlcF9jZWxscyRoZWFsdGh5QGFzc2F5cyRTQ1RAdmFyLmZlYXR1cmVzCgojIEZpdCBHQU0gZm9yIGVhY2ggZ2VuZSB1c2luZyBwc2V1ZG90aW1lIGFzIGluZGVwZW5kZW50IHZhcmlhYmxlLgp0ID0gZGZfZGMkREMxCnQgPSBzY2FsZXM6OnJlc2NhbGUocmFuayhkZl9kYyREQzEpLCBjKDAuMDAwMDEsIDAuOTk5OTkpKQpnZW5lX2ZpdF9wID0gYygpCmdlbmVfZml0X3ZhbHMgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IDE6MTAwKQpmb3IoaSBpbiAxOmxlbmd0aChodmcpKXsKICBnID0gaHZnW2ldCiAgeiA9IGhlcF9jZWxscyRoZWFsdGh5QGFzc2F5cyRTQ1RAZGF0YVtnLF0KICAKICBkID0gZGF0YS5mcmFtZSh6PXosIHQ9dCkKICB0bXAgPSBzdXBwcmVzc01lc3NhZ2VzKGdhbSh6IH4gbnModCwgZGYgPSAzKSwgZGF0YT1kKSkKICAKICAjIGJpbnMgZm9yIG1vZGVsIGZpdHRpbmcKICBiYiA9IHNlcShtaW4odCksIG1heCh0KSwgbGVuZ3RoLm91dCA9IDEwMCkKICBnZW5lX2ZpdF92YWxzWyxnXSA9IHN1cHByZXNzTWVzc2FnZXMocHJlZGljdCh0bXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGRhdGEuZnJhbWUodCA9IGJiKSkpCiAgcCA9IHN1bW1hcnkodG1wKSRwYXJhbWV0cmljLmFub3ZhJGBQcig+RilgWzFdCiAgZ2VuZV9maXRfcCA9IGMoZ2VuZV9maXRfcCwgcCkKICBuYW1lcyhnZW5lX2ZpdF9wKVtsZW5ndGgoZ2VuZV9maXRfcCldID0gZwp9CgpnZW5lX2ZpdF9wID0gZmRydG9vbDo6ZmRydG9vbChnZW5lX2ZpdF9wLCBzdGF0aXN0aWM9InB2YWx1ZSIsIHBsb3Q9RiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2U9RiwgY3V0b2ZmLm1ldGhvZD0icGN0MCIsIHBjdDA9MC45KSRxdmFsCmBgYAoKCiMjIEFuYWx5c2lzIG9mIG90aGVyIGNvbmRpdGlvbnMKTm93IHVzZSB0aGUgdG9wIGdlbmVzIHRvIGxlYXJuIHRoZSBwc2V1ZG96b25hdGlvbiBjb29yZGluYXRlcyBhbmQgcHJvamVjdCB0aGUgZW1ib2xpc2VkIGFuZCByZWdlbmVyYXRpbmcgZGF0YQoKYGBge3J9CnNpZ19nZW5lcyA9IGdlbmVfZml0X3BbZ2VuZV9maXRfcDw9MC4wNV0KdG9wX3NpZ19nZW5lcyA9IG5hbWVzKHNpZ19nZW5lc1tvcmRlcihzaWdfZ2VuZXMsIGRlY3JlYXNpbmcgPSBGKV1bMToxMDAwXSkKdG9wX3NpZ19nZW5lcyA9IHRvcF9zaWdfZ2VuZXNbdG9wX3NpZ19nZW5lcyAlaW4lIHJvd25hbWVzKGhlcF9jZWxscyRlbWJvbGlzZWRAYXNzYXlzJFNDVEBkYXRhKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wX3NpZ19nZW5lcyAlaW4lICByb3duYW1lcyhoZXBfY2VsbHMkcmVnZW5lcmF0aW5nQGFzc2F5cyRTQ1RAZGF0YSldCgpkYXRhX2dhbV9oID0gY2JpbmQoZGF0YS5mcmFtZSgiREMxIiA9IGRmX2RjJERDMSksCiAgICAgICAgICAgICAgICAgICBNYXRyaXg6OnQoaGVwX2NlbGxzJGhlYWx0aEBhc3NheXMkU0NUQGRhdGFbdG9wX3NpZ19nZW5lcyxdKSkKI2RhdGFfZ2FtX2gkREMxID0gc2NhbGVzOjpyZXNjYWxlKC1kYXRhX2dhbV9oJERDMSwgYygwLjAwMDAxLCAwLjk5OTk5KSkKZGF0YV9nYW1faCREQzEgPSBzY2FsZXM6OnJlc2NhbGUocmFuayhkYXRhX2dhbV9oJERDMSksIGMoMC4wMDAwMSwgMC45OTk5OSkpCiNiaW5zX2RjMSA9IGN1dChkYXRhX2dhbV9oJERDMSwgMjAwKQojY2VsbHNfdXNlID0gdW5saXN0KHRhcHBseSgxOmxlbmd0aChiaW5zX2RjMSksIGJpbnNfZGMxLCBmdW5jdGlvbih4KSBzYW1wbGUoeCwgMTIpKSkKCmdhbV9oZWFsdGh5ID0gZ2FtOjpnYW0oREMxIH4gLiwgZGF0YSA9IGRhdGFfZ2FtX2gsIGZhbWlseSA9IG1nY3Y6OmJldGFyKGxpbmsgPSAibG9naXQiLCBlcHMgPSAwLjAwMDAxKSkKI2dhbV9oZWFsdGh5ID0gZ2FtOjpnYW0oREMxIH4gLiwgZGF0YSA9IGRhdGFfZ2FtX2gpCgpoZWFfcHJlZCA9IHByZWRpY3QoZ2FtX2hlYWx0aHksCiAgICAgICAgICAgICAgICAgICBNYXRyaXg6OnQoaGVwX2NlbGxzJGhlYWx0aHlAYXNzYXlzJFNDVEBkYXRhW3RvcF9zaWdfZ2VuZXMsXSksIHR5cGUgPSAicmVzcG9uc2UiKQpyZWdfcHJlZCA9IHByZWRpY3QoZ2FtX2hlYWx0aHksCiAgICAgICAgICAgICAgICAgICBNYXRyaXg6OnQoaGVwX2NlbGxzJHJlZ2VuZXJhdGluZ0Bhc3NheXMkU0NUQGRhdGFbdG9wX3NpZ19nZW5lcyxdKSwgdHlwZSA9ICJyZXNwb25zZSIpCmVtYl9wcmVkID0gcHJlZGljdChnYW1faGVhbHRoeSwKICAgICAgICAgICAgICAgICAgIE1hdHJpeDo6dChoZXBfY2VsbHMkZW1ib2xpc2VkQGFzc2F5cyRTQ1RAZGF0YVt0b3Bfc2lnX2dlbmVzLF0pLCB0eXBlID0gInJlc3BvbnNlIikKCnBsb3QoZGVuc2l0eShyZWdfcHJlZCksIGNvbCA9ICJzYWxtb24iLCB5bGltID0gYygwLDMuMjUpKQpsaW5lcyhkZW5zaXR5KGVtYl9wcmVkKSwgY29sID0gImRhcmtyZWQiKQpsaW5lcyhkZW5zaXR5KGRhdGFfZ2FtX2gkREMxKSwgY29sID0gIm9yYW5nZSIpCmxpbmVzKGRlbnNpdHkoaGVhX3ByZWQpLCBjb2wgPSAiZ3JleTUwIikKbGVnZW5kKC0wLjA3MywzMiwgbGVnZW5kID0gYygiaGVhbHRoeSAoREMxKSIsICJoZWFsdGh5IChwcmVkKSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgImVtYm9saXNlZCAocHJlZCkiLCAicmVnZW5lcmF0aW5nIChwcmVkKSIpLAogICAgICAgZmlsbCA9IGMoImdyZWVuIiwgImdyZXk1MCIsICJyZWQiLCAiYmx1ZSIpKQoKcGxvdF9kZiA9IGRhdGEuZnJhbWUodmFscyA9IGMoZGF0YV9nYW1faCREQzEsIGVtYl9wcmVkLCByZWdfcHJlZCksIAogICAgICAgICAgICAgICAgICAgICBDb25kaXRpb24gPSBjKHJlcCgiaGVhbHRoeSIsIGxlbmd0aChkYXRhX2dhbV9oJERDMSkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoImVtYm9saXNlZCIsIGxlbmd0aChlbWJfcHJlZCkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoInJlZ2VuZXJhdGluZyIsIGxlbmd0aChyZWdfcHJlZCkpKSwKICAgICAgICAgICAgICAgICAgICAgRG9ub3IgPSBjKGhlcF9jZWxscyRoZWFsdGh5JERvbm9yLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlcF9jZWxscyRlbWJvbGlzZWQkRG9ub3IsIGhlcF9jZWxscyRyZWdlbmVyYXRpbmckRG9ub3IpKQpwbG90X2RmJGJpbnMxMDAgPSBjdXQocGxvdF9kZiR2YWxzLCAxMCkKcGxvdF9kZiRDb25kaXRpb24gPSBmYWN0b3IocGxvdF9kZiRDb25kaXRpb24sIGxldmVscyA9IGMoImhlYWx0aHkiLCAiZW1ib2xpc2VkIiwgInJlZ2VuZXJhdGluZyIpKQpnZ3Bsb3QocGxvdF9kZiwgYWVzKHggPSBiaW5zMTAwLCBmaWxsID0gQ29uZGl0aW9uKSkrCiAgICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpKwogICAgIGxhYnMoeCA9ICJ6b25hdGlvbiAoYmlubmVkKSIsIHkgPSAicHJvcG9ydGlvbiIpKwogICAgIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsMCkpKwogICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbGNvbmQpKwogICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gInRvcCIpKSsKICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICBsZWdlbmQudGl0bGUuYWxpZ24gPSAwLAogICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpnZ3Bsb3QocGxvdF9kZiwgYWVzKHggPSBiaW5zMTAwLCBmaWxsID0gQ29uZGl0aW9uKSkrCiAgICAgZmFjZXRfd3JhcCh+Q29uZGl0aW9uKSsKICAgICBnZW9tX2JhcigpKwogICAgIGxhYnMoeCA9ICJ6b25hdGlvbiAoYmlubmVkKSIsIHkgPSAiTnVtYmVyIG9mIGNlbGxzIikrCiAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSkrCiAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sY29uZCkrCiAgICAgY29vcmRfZmxpcCgpKwogICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gInRvcCIpKSsKICAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICBsZWdlbmQudGl0bGUuYWxpZ24gPSAwLAogICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpnZ3Bsb3QocGxvdF9kZiwgYWVzKHggPSB2YWxzLCBjb2xvdXIgPSBEb25vcikpKwogICAgIGZhY2V0X3dyYXAofkNvbmRpdGlvbikrCiAgICAgZ2VvbV9kZW5zaXR5KCkrCiAgICAgbGFicyh4ID0gInpvbmF0aW9uIChiaW5uZWQpIiwgeSA9ICJwcm9wb3J0aW9uIikrCiAgICAgdGhlbWVfYncoKSsKICAgICB0aGVtZShsZWdlbmQudGl0bGUuYWxpZ24gPSAwLAogICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKIyBkYXRhX2dhbV9oJGJpbnMyMCA9IGN1dChkYXRhX2dhbV9oJERDMSwgMjApCiMgbWVhbl9iaW5zID0gYXBwbHkoZGF0YV9nYW1faFssMjo1MDFdLCAyLCBmdW5jdGlvbih4KSB0YXBwbHkoeCwgZGF0YV9nYW1faCRiaW5zMjAsIG1lYW4pKQojIGNvcl9yZWdfYmlucyA9IGNvcihhcy5tYXRyaXgoaGVwX2NlbGxzJHJlZ2VuZXJhdGluZ0Bhc3NheXMkU0NUQGRhdGFbY29sbmFtZXMobWVhbl9iaW5zKSxdKSwgCiMgICAgICAgICAgICAgICAgICAgIHQobWVhbl9iaW5zKSwgbWV0aG9kID0gInNwIikKIyBtYXhfY29yX3JlZ2VuID0gYXBwbHkoY29yX3JlZ19iaW5zLCAxLCB3aGljaC5tYXgpCiMgdGVzdGRmID0gZGF0YS5mcmFtZShiaW5zID0gbWF4X2Nvcl9yZWdlbiwgZG9ucyA9IGhlcF9jZWxscyRyZWdlbmVyYXRpbmckRG9ub3IpCiMgZ2dwbG90KHRlc3RkZiwgYWVzKHggPSBiaW5zLCBmaWxsID0gZG9ucykpK2dlb21fYmFyKCkrZmFjZXRfd3JhcCh+ZG9ucywgc2NhbGVzID0gImZyZWVfeSIpCgpyZWdfZGYgPSBkYXRhLmZyYW1lKCJwdCIgPSByZWdfcHJlZCwgImRvbiIgPSBoZXBfY2VsbHMkcmVnZW5lcmF0aW5nJERvbm9yKQpyZWdfZGYgPSBjYmluZChyZWdfZGYsIE1hdHJpeDo6dChoZXBfY2VsbHMkcmVnZW5lcmF0aW5nQGFzc2F5cyRTQ1RAZGF0YVtnZnJlc2gsXSkpCmdncGxvdChyZWdfZGYsIGFlcyh4ID0gcHQsIHkgPSBTQUExLCBjb2xvdXIgPSBkb24pKSsKICBnZW9tX3BvaW50KCkrCiAgdGhlbWVfY2xhc3NpYygpKwogIGZhY2V0X3dyYXAofmRvbikrCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMykrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygwLDEpKQoKZ2dwbG90KHJlZ19kZiwgYWVzKHggPSBTQUEyLCB5ID0gR0xVTCwgY29sb3VyID0gZG9uKSkrCiAgZ2VvbV9wb2ludCgpKwogIHRoZW1lX2NsYXNzaWMoKSsKICBmYWNldF93cmFwKH5kb24pCgp0cmFqX2xpc3QgPSBsaXN0KCJoZWFsdGh5IiA9IGRhdGFfZ2FtX2gkREMxLCAiZW1ib2xpc2VkIiA9IGVtYl9wcmVkLCAicmVnZW5lcmF0aW5nIiA9IHJlZ19wcmVkKQpgYGAKCkNhbGN1bGF0ZSBjb3JyZWxhdGlvbnMKCmBgYHtyfQpjb3JfaCA9IHQoY29yKHRyYWpfbGlzdCRoZWFsdGh5LCBhcy5tYXRyaXgoTWF0cml4Ojp0KGhlcF9jZWxscyRoZWFsdGh5QGFzc2F5cyRTQ1RAZGF0YSkpLAogICAgICAgICAgICAgIG1ldGhvZCA9ICJzcCIpKQpjb3JfZW1iID0gdChjb3IodHJhal9saXN0JGVtYm9saXNlZCwgYXMubWF0cml4KE1hdHJpeDo6dChoZXBfY2VsbHMkZW1ib2xpc2VkQGFzc2F5cyRTQ1RAZGF0YSkpLAogICAgICAgICAgICAgICAgbWV0aG9kID0gInNwIikpCmNvcl9yZWcgPSB0KGNvcih0cmFqX2xpc3QkcmVnZW5lcmF0aW5nLCBhcy5tYXRyaXgoTWF0cml4Ojp0KGhlcF9jZWxscyRyZWdlbmVyYXRpbmdAYXNzYXlzJFNDVEBkYXRhKSksCiAgICAgICAgICAgICAgICBtZXRob2QgPSAic3AiKSkKCmwgPSBsaXN0KGNvcl9oLCBjb3JfZW1iLCBjb3JfcmVnKQphbGxfY29yciA9IFJlZHVjZShmdW5jdGlvbih4LCB5KSBtZXJnZSh4LHksIGJ5ID0gInJuIiwgYWxsID0gVCksIAogICAgICAgICAgICAgICAgICBsYXBwbHkobCwgZnVuY3Rpb24oeCkgZGF0YS5mcmFtZSh4LCBybiA9IHJvdy5uYW1lcyh4KSkpKQpjb2xuYW1lcyhhbGxfY29ycikgPSBjKCJnZW5lcyIsICJoZWFsdGh5IiwgImVtYm9saXNlZCIsICJyZWdlbmVyYXRpbmciKQoKd3JpdGUuY3N2KGFsbF9jb3JyLCBmaWxlID0gcGFzdGUwKCJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvaGVwX2NvcnJlbGF0aW9uc196b25hdGlvbl9yYW5rLmNzdiIpLAogICAgICAgICAgY29sLm5hbWVzID0gVCwgcm93Lm5hbWVzID0gRiwgcXVvdGUgPSBGKQpgYGAKCkZpbmQgdmFyeWluZyBnZW5lcyAtIHVzaW5nIGFsbCBjZWxscyBhbG9uZyB0aGUgcmFua2VkIHBzZXVkb3RpbWUKCmBgYHtyfQpxdmFsc19saXN0ID0gbGlzdCgpCmZpdHNfbGlzdCA9IGxpc3QoKQoKIyBnZXQgSFZHIGZyb20gYWxsIGNvbmRpdGlvbnMKaHZnID0gdW5pcXVlKGMoaGVwX2NlbGxzW1siaGVhbHRoeSJdXUBhc3NheXMkU0NUQHZhci5mZWF0dXJlcywKICAgICAgICAgICAgICAgaGVwX2NlbGxzW1siZW1ib2xpc2VkIl1dQGFzc2F5cyRTQ1RAdmFyLmZlYXR1cmVzLAogICAgICAgICAgICAgICBoZXBfY2VsbHNbWyJyZWdlbmVyYXRpbmciXV1AYXNzYXlzJFNDVEB2YXIuZmVhdHVyZXMpKQpodmcgPSBodmdbaHZnICVpbiUgcm93bmFtZXMoaGVwX2NlbGxzW1siaGVhbHRoeSJdXUBhc3NheXMkU0NUQGRhdGEpICYKICAgICAgICAgICAgaHZnICVpbiUgcm93bmFtZXMoaGVwX2NlbGxzW1siZW1ib2xpc2VkIl1dQGFzc2F5cyRTQ1RAZGF0YSkgJgogICAgICAgICAgICBodmcgJWluJSByb3duYW1lcyhoZXBfY2VsbHNbWyJyZWdlbmVyYXRpbmciXV1AYXNzYXlzJFNDVEBkYXRhKV0KCmZvcihjb25kIGluIG5hbWVzKGhlcF9jZWxscykpewogIHByaW50KGNvbmQpCiAgIyBGaXQgR0FNIGZvciBlYWNoIGdlbmUgdXNpbmcgcHNldWRvdGltZSBhcyBpbmRlcGVuZGVudCB2YXJpYWJsZS4KICB0ID0gdHJhal9saXN0W1tjb25kXV0KICBnZW5lX2ZpdF9wID0gYygpCiAgZ2VuZV9maXRfdmFscyA9IGRhdGEuZnJhbWUocm93Lm5hbWVzID0gMToxMDApCiAgZm9yKGkgaW4gMTpsZW5ndGgoaHZnKSl7CiAgICBnID0gaHZnW2ldCiAgICB6ID0gaGVwX2NlbGxzW1tjb25kXV1AYXNzYXlzJFNDVEBkYXRhW2csXQogICAgCiAgICBkID0gZGF0YS5mcmFtZSh6PXosIHQ9dCkKICAgIHRtcCA9IHN1cHByZXNzTWVzc2FnZXMoZ2FtKHogfiBucyh0LCBkZiA9IDMpLCBkYXRhPWQpKQogICAgCiAgICAjIGJpbnMgZm9yIG1vZGVsIGZpdHRpbmcKICAgIGJiID0gc2VxKG1pbih0KSwgbWF4KHQpLCBsZW5ndGgub3V0ID0gMTAwKQogICAgZ2VuZV9maXRfdmFsc1ssZ10gPSBzdXBwcmVzc01lc3NhZ2VzKHByZWRpY3QodG1wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGRhdGEuZnJhbWUodCA9IGJiKSkpCiAgICBwID0gc3VtbWFyeSh0bXApJHBhcmFtZXRyaWMuYW5vdmEkYFByKD5GKWBbMV0KICAgIGdlbmVfZml0X3AgPSBjKGdlbmVfZml0X3AsIHApCiAgICBuYW1lcyhnZW5lX2ZpdF9wKVtsZW5ndGgoZ2VuZV9maXRfcCldID0gZwogIH0KICAKICBpZihzdW0oaXMubmEoZ2VuZV9maXRfcCkpPjApewogICAgZ2VuZV9maXRfcFtpcy5uYShnZW5lX2ZpdF9wKV0gPSAxCiAgfQogIGdlbmVfZml0X3AgPSBmZHJ0b29sOjpmZHJ0b29sKGdlbmVfZml0X3AsIHN0YXRpc3RpYz0icHZhbHVlIiwgcGxvdD1GLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlPUYsIGN1dG9mZi5tZXRob2Q9InBjdDAiLCBwY3QwPTAuOSkkcXZhbAogIAogIHF2YWxzX2xpc3RbW2NvbmRdXSA9IGdlbmVfZml0X3AKICBmaXRzX2xpc3RbW2NvbmRdXSA9IGdlbmVfZml0X3ZhbHMKfQpgYGAKClNhdmUgdHJhamVjdG9yaWVzIGFuZCBnZW5lcwoKYGBge3J9CnNhdmUoZml0c19saXN0LCBxdmFsc19saXN0LCB0cmFqX2xpc3QsIAogICAgIGZpbGUgPSAicmVzdWx0cy96b25hdGlvbl9jb25kL2hlcF90cmFqX3F2YWxfZml0c19yYW5rLlJEYXRhIikKYGBgCgpGaW5kIHZhcnlpbmcgZ2VuZXMgLSBub3JtYWxpc2VkIGJ5IGJpbnMgYWxvbmcgdGhlIHJhbmtlZCBwc2V1ZG90aW1lCgpgYGB7cn0KcXZhbHNfYl9saXN0ID0gbGlzdCgpCmZpdHNfYl9saXN0ID0gbGlzdCgpCgojIGdldCBIVkcgZnJvbSBhbGwgY29uZGl0aW9ucwpodmcgPSB1bmlxdWUoYyhoZXBfY2VsbHNbWyJoZWFsdGh5Il1dQGFzc2F5cyRTQ1RAdmFyLmZlYXR1cmVzLAogICAgICAgICAgICAgICBoZXBfY2VsbHNbWyJlbWJvbGlzZWQiXV1AYXNzYXlzJFNDVEB2YXIuZmVhdHVyZXMsCiAgICAgICAgICAgICAgIGhlcF9jZWxsc1tbInJlZ2VuZXJhdGluZyJdXUBhc3NheXMkU0NUQHZhci5mZWF0dXJlcykpCmh2ZyA9IGh2Z1todmcgJWluJSByb3duYW1lcyhoZXBfY2VsbHNbWyJoZWFsdGh5Il1dQGFzc2F5cyRTQ1RAZGF0YSkgJgogICAgICAgICAgICBodmcgJWluJSByb3duYW1lcyhoZXBfY2VsbHNbWyJlbWJvbGlzZWQiXV1AYXNzYXlzJFNDVEBkYXRhKSAmCiAgICAgICAgICAgIGh2ZyAlaW4lIHJvd25hbWVzKGhlcF9jZWxsc1tbInJlZ2VuZXJhdGluZyJdXUBhc3NheXMkU0NUQGRhdGEpXQoKZm9yKGNvbmQgaW4gbmFtZXMoaGVwX2NlbGxzKSl7CiAgcHJpbnQoY29uZCkKICBzZXQuc2VlZCgxKQogICMgZmlsdGVyaW5nIHRoZSBjZWxscyBieSBiaW5zCiAgYmlucyA9IGN1dCh0cmFqX2xpc3RbW2NvbmRdXSwgMTAwKQogIGRvbnMgPSBoZXBfY2VsbHNbW2NvbmRdXSREb25vcgogIG5kb25zID0gcm93U3Vtcyh0YWJsZShiaW5zLCBkb25zKT4xKQogIG5jZWxscyA9IHJvd1N1bXModGFibGUoYmlucywgZG9ucykpCiAgc2FtcGxlaWYgPSBmdW5jdGlvbihjZWxscywgYmlucywgZG9ucyl7CiAgICByZXMgPSBjKCkKICAgIGZvcihkIGluIHVuaXF1ZShkb25zKSl7CiAgICAgIGZvcihiIGluIHVuaXF1ZShiaW5zKSl7CiAgICAgICAgeCA9IGNlbGxzW2JpbnM9PWIgJiBkb25zPT1kXQogICAgICAgIGlmKGxlbmd0aCh4KT49MzApewogICAgICAgICAgcmVzID0gYyhyZXMsIHNhbXBsZSh4LCAzMCwgcmVwbGFjZSA9IEYpKQogICAgICAgIH0gZWxzZSBpZihsZW5ndGgoeCk+PTEgJiBuZG9uc1tiXT49MiAmIG5jZWxsc1tiXT49MTApewogICAgICAgICAgcmVzID0gYyhyZXMsIHgpCiAgICAgICAgfSBlbHNlewogICAgICAgICAgcmVzID0gYyhyZXMsIE5VTEwpCiAgICAgICAgfQogICAgICB9CiAgICB9CiAgICByZXR1cm4ocmVzKQogIH0KICB3aGljaGNlbGxzID0gc2FtcGxlaWYoMTpsZW5ndGgoYmlucyksIGJpbnMsIGRvbnMpCiAgCiAgIyBGaXQgR0FNIGZvciBlYWNoIGdlbmUgdXNpbmcgcHNldWRvdGltZSBhcyBpbmRlcGVuZGVudCB2YXJpYWJsZS4KICB0ID0gdHJhal9saXN0W1tjb25kXV1bd2hpY2hjZWxsc10KICBnZW5lX2ZpdF9wID0gYygpCiAgZ2VuZV9maXRfdmFscyA9IGRhdGEuZnJhbWUocm93Lm5hbWVzID0gMToxMDApCiAgZm9yKGkgaW4gMTpsZW5ndGgoaHZnKSl7CiAgICBnID0gaHZnW2ldCiAgICB6ID0gaGVwX2NlbGxzW1tjb25kXV1AYXNzYXlzJFNDVEBkYXRhW2csd2hpY2hjZWxsc10KICAgIAogICAgZCA9IGRhdGEuZnJhbWUoej16LCB0PXQpCiAgICB0bXAgPSBzdXBwcmVzc01lc3NhZ2VzKGdhbSh6IH4gbnModCwgZGYgPSAzKSwgZGF0YT1kKSkKICAgIAogICAgIyBiaW5zIGZvciBtb2RlbCBmaXR0aW5nCiAgICBiYiA9IHNlcShtaW4odCksIG1heCh0KSwgbGVuZ3RoLm91dCA9IDEwMCkKICAgIGdlbmVfZml0X3ZhbHNbLGddID0gc3VwcHJlc3NNZXNzYWdlcyhwcmVkaWN0KHRtcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBkYXRhLmZyYW1lKHQgPSBiYikpKQogICAgcCA9IHN1bW1hcnkodG1wKSRwYXJhbWV0cmljLmFub3ZhJGBQcig+RilgWzFdCiAgICBnZW5lX2ZpdF9wID0gYyhnZW5lX2ZpdF9wLCBwKQogICAgbmFtZXMoZ2VuZV9maXRfcClbbGVuZ3RoKGdlbmVfZml0X3ApXSA9IGcKICB9CiAgCiAgaWYoc3VtKGlzLm5hKGdlbmVfZml0X3ApKT4wKXsKICAgIGdlbmVfZml0X3BbaXMubmEoZ2VuZV9maXRfcCldID0gMQogIH0KICBnZW5lX2ZpdF9wID0gZmRydG9vbDo6ZmRydG9vbChnZW5lX2ZpdF9wLCBzdGF0aXN0aWM9InB2YWx1ZSIsIHBsb3Q9RiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1GLCBjdXRvZmYubWV0aG9kPSJwY3QwIiwgcGN0MD0wLjkpJHF2YWwKICAKICBxdmFsc19iX2xpc3RbW2NvbmRdXSA9IGdlbmVfZml0X3AKICBmaXRzX2JfbGlzdFtbY29uZF1dID0gZ2VuZV9maXRfdmFscwp9CmBgYAoKU2F2ZSB0cmFqZWN0b3JpZXMgYW5kIGdlbmVzCgpgYGB7cn0Kc2F2ZShmaXRzX2JfbGlzdCwgcXZhbHNfYl9saXN0LCB0cmFqX2xpc3QsIAogICAgIGZpbGUgPSAicmVzdWx0cy96b25hdGlvbl9jb25kL2hlcF9iaW5uZWRfdHJhal9xdmFsX2ZpdHNfcmFuay5SRGF0YSIpCmBgYAoKRmluZCB2YXJ5aW5nIGdlbmVzIC0gdXNpbmcgZWFjaCBkb25vciBhcyBhIHJlcGxpY2F0ZSBhbmQgY2FsY3VsYXRpbmcgdGhlIGF2ZXJhZ2UgZXhwcmVzc2lvbiBmb3IgZWFjaCBvZiAxMDAgYmlucwoKYGBge3J9CnF2YWxzX2RiX2xpc3QgPSBsaXN0KCkKZml0c19kYl9saXN0ID0gbGlzdCgpCgojIGdldCBIVkcgZnJvbSBhbGwgY29uZGl0aW9ucwpodmcgPSB1bmlxdWUoYyhoZXBfY2VsbHNbWyJoZWFsdGh5Il1dQGFzc2F5cyRTQ1RAdmFyLmZlYXR1cmVzLAogICAgICAgICAgICAgICBoZXBfY2VsbHNbWyJlbWJvbGlzZWQiXV1AYXNzYXlzJFNDVEB2YXIuZmVhdHVyZXMsCiAgICAgICAgICAgICAgIGhlcF9jZWxsc1tbInJlZ2VuZXJhdGluZyJdXUBhc3NheXMkU0NUQHZhci5mZWF0dXJlcykpCmh2ZyA9IGh2Z1todmcgJWluJSByb3duYW1lcyhoZXBfY2VsbHNbWyJoZWFsdGh5Il1dQGFzc2F5cyRTQ1RAZGF0YSkgJgogICAgICAgICAgICBodmcgJWluJSByb3duYW1lcyhoZXBfY2VsbHNbWyJlbWJvbGlzZWQiXV1AYXNzYXlzJFNDVEBkYXRhKSAmCiAgICAgICAgICAgIGh2ZyAlaW4lIHJvd25hbWVzKGhlcF9jZWxsc1tbInJlZ2VuZXJhdGluZyJdXUBhc3NheXMkU0NUQGRhdGEpXQoKbWV0YV9kZiA9IGRhdGEuZnJhbWUoInB0IiA9IGModHJhal9saXN0JGhlYWx0aHksIHRyYWpfbGlzdCRlbWJvbGlzZWQsIHRyYWpfbGlzdCRyZWdlbmVyYXRpbmcpLAogICAgICAgICAgICAgICAgICAgICAiZG9ub3JzIiA9IGMoaGVwX2NlbGxzJGhlYWx0aHkkRG9ub3IsIGhlcF9jZWxscyRlbWJvbGlzZWQkRG9ub3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZXBfY2VsbHMkcmVnZW5lcmF0aW5nJERvbm9yKSwKICAgICAgICAgICAgICAgICAgICAgImNvbmQiID0gYyhoZXBfY2VsbHMkaGVhbHRoeSRDb25kaXRpb24sIGhlcF9jZWxscyRlbWJvbGlzZWQkQ29uZGl0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVwX2NlbGxzJHJlZ2VuZXJhdGluZyRDb25kaXRpb24pKQptZXRhX2RmJGJpbnMgPSBjdXQobWV0YV9kZiRwdCwgMTAwKQoKZm9yKGNvbmQgaW4gdW5pcXVlKG1ldGFfZGYkY29uZCkpewogIHByaW50KGNvbmQpCiAgc2V0LnNlZWQoMSkKICAKICAjIEZpdCBHQU0gZm9yIGVhY2ggZ2VuZQogIHQgPSAxOjEwMAogIHN1Yl9tZXRhX2RmID0gbWV0YV9kZlttZXRhX2RmJGNvbmQ9PWNvbmQsXQogIGdlbmVfZml0X3AgPSBjKCkKICBnZW5lX2ZpdF92YWxzID0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSAxOjEwMCkKICBmb3IoaSBpbiAxOmxlbmd0aChodmcpKXsKICAgIGcgPSBodmdbaV0KICAgIHogPSBoZXBfY2VsbHNbW2NvbmRdXUBhc3NheXMkU0NUQGRhdGFbZyxdCiAgICAKICAgIGQgPSBhZ2dyZWdhdGUoen5iaW5zK2Rvbm9ycywgZGF0YSA9IGNiaW5kKHN1Yl9tZXRhX2RmLCB6KSwgRlVOPSJtZWFuIikKICAgIGNjID0gZGF0YS5mcmFtZSh0YWJsZShzdWJfbWV0YV9kZiRiaW5zLCBhcy5mYWN0b3IoYXMuY2hhcmFjdGVyKHN1Yl9tZXRhX2RmJGRvbm9ycykpKSkKICAgIGNjID0gY2NbY2MkRnJlcT09MCxdCiAgICBjb2xuYW1lcyhjYykgPSBjb2xuYW1lcyhkKQogICAgZCA9IHJiaW5kKGQsIGNjKQogICAgZCA9IGRbb3JkZXIoZCRiaW5zLCBkZWNyZWFzaW5nID0gRiksXQogICAgZCR0ID0gcmVwKHQsIGVhY2ggPSBsZW5ndGgodW5pcXVlKGQkZG9ub3JzKSkpCiAgICB0bXAgPSBzdXBwcmVzc01lc3NhZ2VzKGdhbSh6IH4gbnModCwgZGYgPSAzKSwgZGF0YT1kKSkKICAgIAogICAgIyBiaW5zIGZvciBtb2RlbCBmaXR0aW5nCiAgICBiYiA9IHNlcShtaW4odCksIG1heCh0KSwgbGVuZ3RoLm91dCA9IDEwMCkKICAgIGdlbmVfZml0X3ZhbHNbLGddID0gc3VwcHJlc3NNZXNzYWdlcyhwcmVkaWN0KHRtcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBkYXRhLmZyYW1lKHQgPSBiYikpKQogICAgcCA9IHN1bW1hcnkodG1wKSRwYXJhbWV0cmljLmFub3ZhJGBQcig+RilgWzFdCiAgICBnZW5lX2ZpdF9wID0gYyhnZW5lX2ZpdF9wLCBwKQogICAgbmFtZXMoZ2VuZV9maXRfcClbbGVuZ3RoKGdlbmVfZml0X3ApXSA9IGcKICB9CiAgCiAgaWYoc3VtKGlzLm5hKGdlbmVfZml0X3ApKT4wKXsKICAgIGdlbmVfZml0X3BbaXMubmEoZ2VuZV9maXRfcCldID0gMQogIH0KICBnZW5lX2ZpdF9wID0gZmRydG9vbDo6ZmRydG9vbChnZW5lX2ZpdF9wLCBzdGF0aXN0aWM9InB2YWx1ZSIsIHBsb3Q9RiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1GLCBjdXRvZmYubWV0aG9kPSJwY3QwIiwgcGN0MD0wLjkpJHF2YWwKICAKICBxdmFsc19kYl9saXN0W1tjb25kXV0gPSBnZW5lX2ZpdF9wCiAgZml0c19kYl9saXN0W1tjb25kXV0gPSBnZW5lX2ZpdF92YWxzCn0KYGBgCgpTYXZlIHRyYWplY3RvcmllcyBhbmQgZ2VuZXMKCmBgYHtyfQpzYXZlKGZpdHNfZGJfbGlzdCwgcXZhbHNfZGJfbGlzdCwgdHJhal9saXN0LCAKICAgICBmaWxlID0gInJlc3VsdHMvem9uYXRpb25fY29uZC9oZXBfYmlubmVkRG9ub3JfdHJhal9xdmFsX2ZpdHNfcmFuay5SRGF0YSIpCmBgYAoKUGxvdCBjZWxsIGRpc3RyaWJ1dGlvbnMgaW4gcHNldWRvdGltZQoKYGBge3J9CnBsb3RfZGYgPSBkYXRhLmZyYW1lKCJwc2V1ZG90aW1lIiA9IGModHJhal9saXN0JGhlYWx0aHksIHRyYWpfbGlzdCRlbWJvbGlzZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhal9saXN0JHJlZ2VuZXJhdGluZyksCiAgICAgICAgICAgImRvbm9yIiA9IGMoaGVwX2NlbGxzJGhlYWx0aHlAbWV0YS5kYXRhWywiRG9ub3IiXSwKICAgICAgICAgICAgICAgICAgICAgICBoZXBfY2VsbHMkZW1ib2xpc2VkQG1ldGEuZGF0YVtuYW1lcyh0cmFqX2xpc3QkZW1ib2xpc2VkKSwiRG9ub3IiXSwKICAgICAgICAgICAgICAgICAgICAgICBoZXBfY2VsbHMkcmVnZW5lcmF0aW5nQG1ldGEuZGF0YVtuYW1lcyh0cmFqX2xpc3QkcmVnZW5lcmF0aW5nKSwiRG9ub3IiXSksCiAgICAgICAgICAgImNvbmQiID0gYyhyZXAoImhlYWx0aHkiLCBsZW5ndGgodHJhal9saXN0JGhlYWx0aHkpKSwgCiAgICAgICAgICAgICAgICAgICAgICByZXAoImVtYm9saXNlZCIsIGxlbmd0aCh0cmFqX2xpc3QkZW1ib2xpc2VkKSksCiAgICAgICAgICAgICAgICAgICAgICByZXAoInJlZ2VuZXJhdGluZyIsIGxlbmd0aCh0cmFqX2xpc3QkcmVnZW5lcmF0aW5nKSkpKQoKcGRmKCJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvaGVwX2NlbGxfZGlzdHJpYnV0aW9uc196b25hdGlvbi5wZGYiLCB1c2VEaW5nYmF0cyA9IEYsIAogICAgd2lkdGggPSA2LCBoZWlnaHQgPSA1KQpnZ3Bsb3QocGxvdF9kZiwgYWVzKHggPSBkb25vciwgeSA9IHBzZXVkb3RpbWUsIGZpbGwgPSBjb25kKSkrCiAgZ2VvbV9ib3hwbG90KG5vdGNoID0gVCkrCiAgdGhlbWVfYncoKQoKZ2dwbG90KHBsb3RfZGYsIGFlcyh4ID0gcHNldWRvdGltZSwgZ3JvdXAgPSBkb25vciwgY29sb3VyID0gZG9ub3IpKSsKICBmYWNldF93cmFwKH5jb25kKSsKICBnZW9tX2RlbnNpdHkoKSsKICB0aGVtZV9idygpCgpnZ3Bsb3QocGxvdF9kZiwgYWVzKHggPSBwc2V1ZG90aW1lLCBjb2xvdXIgPSBjb25kKSkrCiAgZ2VvbV9kZW5zaXR5KCkrCiAgdGhlbWVfYncoKQpkZXYub2ZmKCkKYGBgCgpEbyBjcm9zcyB2YWxpZGF0aW9uIG9uIHRoZSBoZWFsdGh5IHRyYWplY3RvcnkKCmBgYHtyfQpzaWdfZ2VuZXMgPSBxdmFsc19saXN0JGhlYWx0aHlbcXZhbHNfbGlzdCRoZWFsdGh5PD0wLjA1XQp0b3Bfc2lnX2dlbmVzID0gbmFtZXMoc2lnX2dlbmVzW29yZGVyKHNpZ19nZW5lcywgZGVjcmVhc2luZyA9IEYpXVsxOjUwMF0pIAoKZGF0YV9nYW1faCA9IGNiaW5kKGRhdGEuZnJhbWUoIkRDMSIgPSB0cmFqX2xpc3QkaGVhbHRoeSksCiAgICAgICAgICAgICAgICAgICBNYXRyaXg6OnQoaGVwX2NlbGxzJGhlYWx0aEBhc3NheXMkU0NUQGRhdGFbdG9wX3NpZ19nZW5lcyxdKSkKY29sbmFtZXMoZGF0YV9nYW1faCkgPSBnc3ViKCItIiwgIl8iLCBjb2xuYW1lcyhkYXRhX2dhbV9oKSwgZml4ZWQgPSBUKQpjb2xuYW1lcyhkYXRhX2dhbV9oKSA9IGdzdWIoIi4iLCAiXyIsIGNvbG5hbWVzKGRhdGFfZ2FtX2gpLCBmaXhlZCA9IFQpCgpjdl9nYW0gPSBDVmdhbShmb3JtdWxhID0gREMxIH4gLiwgZGF0YSA9IGRhdGFfZ2FtX2gsIG5mb2xkID0gMTAsIHNlZWQgPSAxLCBtZXRob2QgPSAiZ2xtLmZpdCIpCgpwZGYoInJlc3VsdHMvem9uYXRpb25faGVhbHRoeS9oZXBfb3JpZ2luYWxfZml0dGVkX3BzZXVkb3RpbWUucGRmIiwgdXNlRGluZ2JhdHMgPSBGLCAKICAgIHdpZHRoID0gNiwgaGVpZ2h0ID0gNSkKcGxvdChkYXRhX2dhbV9oJERDMSwgY3ZfZ2FtJGZpdHRlZCwgeGxhYiA9ICJIZWFsdGh5IHBzZXVkb3RpbWUgKERDMSkiLHlsYWIgPSAiUHJlZGljdGVkIEhlYWx0aHkiLAogICAgIG1haW4gPSBwYXN0ZTAoIkhlYWx0aHkgb3JpZ2luYWwgdnMgZml0dGVkIHBzZXVkb3RpbWVcbk1TRTogIiwgcm91bmQoY3ZfZ2FtJGN2c2NhbGUsIDYpKSwKICAgICBwY2ggPSAxOSwgY2V4ID0gMC41KQphYmxpbmUoMCwxLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDMpCmRldi5vZmYoKQpgYGAKClNhdmUgem9uYXRpb24gcmVzdWx0cwoKYGBge3J9CiMgZGVmaW5lIHRoZSBpbnRlcnZhbHMgYmFzZWQgb24gaGVhbHRoeQpkZl9oID0gZGF0YS50YWJsZSgiem9uYXRpb25fcHQiID0gdHJhal9saXN0JGhlYWx0aHkpCmRmX2gkem9uYXRpb25faW50ID0gY3V0KGRmX2gkem9uYXRpb25fcHQsIDMpCgpkZl9lID0gZGF0YS50YWJsZSgiem9uYXRpb25fcHQiID0gdHJhal9saXN0JGVtYm9saXNlZCkKZGZfZSA9IGRmX2hbZGZfZSwgb249InpvbmF0aW9uX3B0Iiwgcm9sbD1JbmYsIHJvbGxlbmRzID0gVF0KCmRmX3IgPSBkYXRhLnRhYmxlKCJ6b25hdGlvbl9wdCIgPSB0cmFqX2xpc3QkcmVnZW5lcmF0aW5nKQpkZl9yID0gZGZfaFtkZl9yLCBvbj0iem9uYXRpb25fcHQiLCByb2xsPUluZiwgcm9sbGVuZHMgPSBUXQoKbGlzdF9kZiA9IGxpc3QoImhlYWx0aHkiID0gZGZfaCwgImVtYm9saXNlZCIgPSBkZl9lLCAicmVnZW5lcmF0aW5nIiA9IGRmX3IpCgpmb3IobiBpbiBuYW1lcyh0cmFqX2xpc3QpKXsKICBwdGRmID0gbGlzdF9kZltbbl1dCiAgcm93bmFtZXMocHRkZikgPSBjb2xuYW1lcyhoZXBfY2VsbHNbW25dXSkKICBoZXBfY2VsbHNbW25dXSA9IEFkZE1ldGFEYXRhKGhlcF9jZWxsc1tbbl1dLCBtZXRhZGF0YSA9IHB0ZGYpCiAgd3JpdGUuY3N2KGhlcF9jZWxsc1tbbl1dQG1ldGEuZGF0YVssYygidW5pcXVlX25hbWUiLCAiQ29uZGl0aW9uIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiem9uYXRpb25fcHQiLCAiem9uYXRpb25faW50IildLAogICAgICAgICAgICBmaWxlID0gcGFzdGUwKCJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvaGVwX3pvbmF0aW9uXyIsIG4sICJfcmFuay5jc3YiKSwKICAgICAgICAgICAgY29sLm5hbWVzID0gVCwgcm93Lm5hbWVzID0gVCwgcXVvdGUgPSBGKQp9CgpzYXZlUkRTKGhlcF9jZWxscywgZmlsZSA9ICJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvaGVwX2NlbGxzX3pvbmF0aW9uX3JhbmsuUkRTIikKYGBgCgoKCiMgRW5kb3RoZWxpYWwgY2VsbHMKU3Vic2V0IGFuZCBwcm9jZXNzIGVhY2ggY29uZGl0aW9uIChvbmx5IExTRUMpCgpgYGB7cn0KZW5kX2NlbGxzID0gbGlzdCgpCmZvcihjb25kIGluIHVuaXF1ZShhbGxjZWxsc19jc3NAbWV0YS5kYXRhJENvbmRpdGlvbikpewogIGNhdChjb25kKQogIGVuZF9jZWxsc1tbY29uZF1dID0gYWxsY2VsbHNfY3NzWyxncmVwbCgiTFNFQyAiLCBhbGxjZWxsc19jc3NAbWV0YS5kYXRhJGFsbGNlbGxzX2NsdXN0ZXJzKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbGxjZWxsc19jc3NAbWV0YS5kYXRhJENvbmRpdGlvbj09Y29uZF0KICBpZihjb25kPT0iaGVhbHRoeSIpewogICAgZW5kX2NlbGxzW1tjb25kXV0gPSBlbmRfY2VsbHNbW2NvbmRdXVssZ3JlcGwoIkxTRUMgIiwgZW5kX2NlbGxzW1tjb25kXV1AbWV0YS5kYXRhJG5hbWVzX2NsdXN0ZXJzKV0KICB9CiAgcHJpbnQoZGltKGVuZF9jZWxsc1tbY29uZF1dKSkKICBlbmRfY2VsbHNbW2NvbmRdXSA9IHN1cHByZXNzV2FybmluZ3MoU0NUcmFuc2Zvcm0oZW5kX2NlbGxzW1tjb25kXV0sIGRvLmNvcnJlY3QudW1pID0gVCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFycy50by5yZWdyZXNzPWMoInVuaXF1ZV9uYW1lIiwibkNvdW50X1JOQSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5ydi50aCA9IDEsIHNlZWQudXNlID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuLm9ubHkudmFyLmdlbmVzID0gRiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLmZlYXR1cmVzLm4gPSBOVUxMKSkKICBlbmRfY2VsbHNbW2NvbmRdXSA9IFJ1blBDQShlbmRfY2VsbHNbW2NvbmRdXSwgdmVyYm9zZSA9IEYpCiAgZW5kX2NlbGxzW1tjb25kXV0gPSBSdW5VTUFQKGVuZF9jZWxsc1tbY29uZF1dLCBkaW1zID0gMToyMCwgdmVyYm9zZSA9IEYpCn0KYGBgCgoKIyMgSGVhbHRoeSBhbmFseXNpcwpGb3IgaGVhbHRoeSBjZWxscyAod2hpY2ggd2lsbCBzZXJ2ZSBhcyByZWZlcmVuY2UpLCBkbyBhZGRpdGlvbmFsIGZpbHRlcmluZyBhbmQgcmVwcm9jZXNzaW5nCgpgYGB7cn0KRGltUGxvdChlbmRfY2VsbHMkaGVhbHRoeSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJhbGxjZWxsc19jbHVzdGVycyIpCmdmcmVzaCA9IGMoIkNMRUMxQiIsICJDTEVDNEciLCAiTFlWRTEiLCAiQ0QxNCIsICAjIGNlbnRyYWwgCiAgICAgICAgICAgIlBFQ0FNMSIsICJBUVAxIiwgIlZXRiIsICJDRDM0IiwgIyBwb3J0YWwKICAgICAgICAgICAiQUxCIiwgIk1LSTY3IiwgIlRSQUMiLCAiRENOIikgCkZlYXR1cmVQbG90KGVuZF9jZWxscyRoZWFsdGh5LCBmZWF0dXJlcyA9IGdmcmVzaCwgcmVkdWN0aW9uID0gInVtYXAiKQoKb3V0Y2VsbHMgPSAoZW5kX2NlbGxzJGhlYWx0aHlAcmVkdWN0aW9ucyR1bWFwQGNlbGwuZW1iZWRkaW5nc1ssMV08KC00KSkgIyB0aGlzIHJlbW92ZXMgd2hhdCBzZWVtIHRvIGJlIGNlbnRyYWwgaGVwYXRvY3l0ZXMKZW5kX2NlbGxzJGhlYWx0aHkgPSBlbmRfY2VsbHMkaGVhbHRoeVssIW91dGNlbGxzXQoKZW5kX2NlbGxzJGhlYWx0aHkgPSBzdXBwcmVzc1dhcm5pbmdzKFNDVHJhbnNmb3JtKGVuZF9jZWxscyRoZWFsdGh5LCBkby5jb3JyZWN0LnVtaSA9IFQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJzLnRvLnJlZ3Jlc3MgPSBjKCJ1bmlxdWVfbmFtZSIsIm5Db3VudF9STkEiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLmZlYXR1cmVzLnJ2LnRoID0gMSwgc2VlZC51c2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuLm9ubHkudmFyLmdlbmVzID0gRiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5uID0gTlVMTCkpCmVuZF9jZWxscyRoZWFsdGh5ID0gUnVuUENBKGVuZF9jZWxscyRoZWFsdGh5LCB2ZXJib3NlID0gRikKZW5kX2NlbGxzJGhlYWx0aHkgPSBSdW5VTUFQKGVuZF9jZWxscyRoZWFsdGh5LCBkaW1zID0gMToxNSwgdmVyYm9zZSA9IEYpCkRpbVBsb3QoZW5kX2NlbGxzJGhlYWx0aHksIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAidW5pcXVlX25hbWUiKQoKZW5kX2NlbGxzJGhlYWx0aHkgPSBGaW5kTmVpZ2hib3JzKGVuZF9jZWxscyRoZWFsdGh5LCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JjZS5yZWNhbGMgPSBULCBncmFwaC5uYW1lID0gImVuZCIpCmVuZF9jZWxscyRoZWFsdGh5ID0gRmluZENsdXN0ZXJzKGVuZF9jZWxscyRoZWFsdGh5LCBncmFwaC5uYW1lID0gImVuZCIsIGFsZ29yaXRobSA9IDIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNvbHV0aW9uID0gc2VxKDAuMSwwLjgsMC4xKSwgdmVyYm9zZSA9IEYpCkRpbVBsb3QoZW5kX2NlbGxzJGhlYWx0aHksIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiZW5kX3Jlcy4wLjIiKQplbmRfY2VsbHMkaGVhbHRoeSA9IFNldElkZW50KGVuZF9jZWxscyRoZWFsdGh5LCB2YWx1ZSA9ICJlbmRfcmVzLjAuMiIpCm1rX2VuZF9oID0gRmluZEFsbE1hcmtlcnMoZW5kX2NlbGxzJGhlYWx0aHksIGFzc2F5ID0gIlNDVCIpCgpzYXZlUkRTKGVuZF9jZWxscyRoZWFsdGh5LCBmaWxlID0gInJlc3VsdHMvem9uYXRpb25faGVhbHRoeS9lbmRfaGVhbHRoeV9zcmF0LlJEUyIpCmBgYAoKU2F2ZSBlbmRvdGhlbGlhbCBjZWxscwoKYGBge3J9CnNhdmVSRFMoZW5kX2NlbGxzLCBmaWxlPSJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvZW5kX2NlbGxzLlJEUyIpCmBgYAoKUGxvdCBleHByZXNzaW9uIG9mIHpvbmF0aW9uIG1hcmtlcnMKCmBgYHtyfQpnZnJlc2ggPSBjKCJDTEVDMUIiLCAiQ0xFQzRHIiwgIkxZVkUxIiwgIkNEMTQiLCAgIyBjZW50cmFsIAogICAgICAgICAgICJQRUNBTTEiLCAiQVFQMSIsICJWV0YiLCAiQ0QzNCIpICMgcG9ydGFsCgpwbmcoInJlc3VsdHMvem9uYXRpb25faGVhbHRoeS9oZWFsdGh5X2hlcGF0b2N5dGVfem9uYXRpb25fbWFya2Vyc192aW9saW4ucG5nIiwgCiAgICBoZWlnaHQgPSAxNDAwLCB3aWR0aCA9IDE0MDApClZsblBsb3QoZW5kX2NlbGxzJGhlYWx0aHksIGZlYXR1cmVzID0gZ2ZyZXNoLCBncm91cC5ieSA9ICJuYW1lc19jbHVzdGVycyIsIAogICAgICAgIHB0LnNpemUgPSAwLCBzb3J0ID0gImluY3JlYXNpbmciKQpkZXYub2ZmKCkKcG5nKCJyZXN1bHRzL3pvbmF0aW9uX2hlYWx0aHkvaGVhbHRoeV9oZXBhdG9jeXRlX3pvbmF0aW9uX21hcmtlcnNfdW1hcC5wbmciLCAKICAgIGhlaWdodCA9IDE0MDAsIHdpZHRoID0gMTQwMCkKRmVhdHVyZVBsb3QoZW5kX2NlbGxzJGhlYWx0aHksIGZlYXR1cmVzID0gZ2ZyZXNoLCBwdC5zaXplID0gMC42KQpkZXYub2ZmKCkKYGBgCgpPYnRhaW4gRGlmZnVzaW9uIG1hcHMgcHJvamVjdGlvbgoKYGBge3J9CnNldC5zZWVkKDEpCmRtID0gRGlmZnVzaW9uTWFwKGVuZF9jZWxscyRoZWFsdGh5QHJlZHVjdGlvbnMkcGNhQGNlbGwuZW1iZWRkaW5nc1ssMTo0XSwgCiAgICAgICAgICAgICAgICAgIHJvdGF0ZSA9IFQsIG5fZWlncyA9IDUpCmRwdCA9IERQVChkbSwgdGlwcyA9IGMoMjM4MiwgMTM3OSwgNDI0KSkKcGxvdChkcHQsIGNvbF9ieSA9ICJEUFQ0MjQiKQpwbG90KGRwdCwgY29sX2J5ID0gImJyYW5jaCIpCgpkbSA9IERpZmZ1c2lvbk1hcChlbmRfY2VsbHMkaGVhbHRoeUByZWR1Y3Rpb25zJHBjYUBjZWxsLmVtYmVkZGluZ3NbZHB0JERQVDQyNDwxNSwxOjRdLCAKICAgICAgICAgICAgICAgICAgcm90YXRlID0gVCwgbl9laWdzID0gNSkKZHB0ID0gRFBUKGRtKQpwbG90KGRwdCwgY29sX2J5ID0gIkRQVDM2MyIpCgplbmRfY2VsbHMkaGVhbHRoeSA9IGVuZF9jZWxscyRoZWFsdGh5WyxuYW1lcyhkcHRAZG0kREMxKV0KYGBgCgpDaGVjayBleHByZXNzaW9uIG9mIHNvbWUgZ2VuZXMKCmBgYHtyfQpkZl9kYyA9IGNiaW5kKGVuZF9jZWxscyRoZWFsdGh5QG1ldGEuZGF0YSwgCiAgICAgICAgICAgICAgZGF0YS5mcmFtZSgiREMxIiA9IGRwdEBkbSREQzEsICJEQzIiID0gZHB0QGRtJERDMiwgIkRQVCIgPSBkcHQkRFBUMzYzKSwKICAgICAgICAgICAgICBNYXRyaXg6OnQoZW5kX2NlbGxzJGhlYWx0aHlAYXNzYXlzJFNDVEBkYXRhW2dmcmVzaCxdKSkKY29sbmFtZXMoZGZfZGMpID0gZ3N1YigiLSIsICIuIiwgY29sbmFtZXMoZGZfZGMpLCBmaXhlZCA9IFQpCmRmX2RjID0gY2JpbmQoZGZfZGMsIGVuZF9jZWxscyRoZWFsdGh5QHJlZHVjdGlvbnMkdW1hcEBjZWxsLmVtYmVkZGluZ3MpCgpwbHRfbGlzdCA9IGxpc3QoKQpmb3IobiBpbiBjKGdzdWIoIi0iLCAiLiIsIGdmcmVzaCwgZml4ZWQgPSBUKSwgIm5hbWVzX2NsdXN0ZXJzIiwgInVuaXF1ZV9uYW1lIikpewogIHBsdF9saXN0W1tuXV0gPSBnZ3Bsb3QoZGZfZGMsIGFlc19zdHJpbmcoeCA9ICJEQzEiLCB5ID0gIkRDMiIsIGNvbG91ciA9IG4pKSsKICAgIGdlb21fcG9pbnQoKSsKICAgIHRoZW1lX2NsYXNzaWMoKQp9CgpwbmcoInJlc3VsdHMvem9uYXRpb25faGVhbHRoeS9oZWFsdGh5X2VuZG90aGVsaWFsX3pvbmF0aW9uX21hcmtlcnNfZGMucG5nIiwgCiAgICBoZWlnaHQgPSA5MDAsIHdpZHRoID0gMjgwMCkKY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gcGx0X2xpc3QsIG5yb3cgPSA0LCBuY29sID0gMywgYWxpZ24gPSAiaHYiKQpkZXYub2ZmKCkKYGBgCgpXZSdsbCBiZSB1c2luZyBEUFQzNjMgYXMgdGhlIHBzZXVkb3RpbWUuIFRoaXMgd2FzIHRoZSBiZXN0IGFwcHJveGltYXRpb24gcG9zc2libGUgdG8gdGhlIHpvbmF0aW9uIGdyYWRpZW50IGJhc2VkIG9uIGRpZmZ1c2lvbiBtYXBzCgpgYGB7cn0KcGRmKCJyZXN1bHRzL3pvbmF0aW9uX2hlYWx0aHkvZW5kX2Rpc3RyaWJ1dGlvbnNfRFBUMzYzLnBkZiIsIHVzZURpbmdiYXRzID0gRiwgCiAgICBoZWlnaHQgPSA0LCB3aWR0aCA9IDkpCnBsdDEgPSBnZ3Bsb3QoZGZfZGMsIGFlcyh4ID0gRFBULCB5ID0gbmFtZXNfY2x1c3RlcnMsIAogICAgICAgICAgICAgICAgICBncm91cCA9IG5hbWVzX2NsdXN0ZXJzLCBmaWxsID0gbmFtZXNfY2x1c3RlcnMpKSsKICBnZW9tX2RlbnNpdHlfcmlkZ2VzMihhbHBoYSA9IDAuNCkrCiAgdGhlbWVfYncoKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpwbHQyID0gZ2dwbG90KGRmX2RjLCBhZXMoeCA9IERQVCwgeSA9IHVuaXF1ZV9uYW1lLCAKICAgICAgICAgICAgICAgICAgZ3JvdXAgPSB1bmlxdWVfbmFtZSwgZmlsbCA9IHVuaXF1ZV9uYW1lKSkrCiAgZ2VvbV9kZW5zaXR5X3JpZGdlczIoYWxwaGEgPSAwLjQpKwogIHRoZW1lX2J3KCkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKY293cGxvdDo6cGxvdF9ncmlkKHBsdDEsIHBsdDIsIG5jb2wgPSAyLCBhbGlnbiA9ICJoIikKZGV2Lm9mZigpCmBgYAoKRmluZCBnZW5lcyB2YXJ5aW5nIGFsb25nIHRoZSBwc2V1ZG90aW1lCgpgYGB7cn0KIyB3aWxsIG9ubHkgdXNlIHZhcmlhYmxlIGdlbmVzCmh2ZyA9IGVuZF9jZWxscyRoZWFsdGh5QGFzc2F5cyRTQ1RAdmFyLmZlYXR1cmVzCgojIEZpdCBHQU0gZm9yIGVhY2ggZ2VuZSB1c2luZyBwc2V1ZG90aW1lIGFzIGluZGVwZW5kZW50IHZhcmlhYmxlLgp0ID0gc2NhbGVzOjpyZXNjYWxlKHJhbmsoLWRmX2RjJERQVCksIGMoMC4wMDAwMSwgMC45OTk5OSkpCmdlbmVfZml0X3AgPSBjKCkKZ2VuZV9maXRfdmFscyA9IGRhdGEuZnJhbWUocm93Lm5hbWVzID0gMToxMDApCmZvcihpIGluIDE6bGVuZ3RoKGh2ZykpewogIGcgPSBodmdbaV0KICB6ID0gZW5kX2NlbGxzJGhlYWx0aHlAYXNzYXlzJFNDVEBkYXRhW2csXQogIAogIGQgPSBkYXRhLmZyYW1lKHo9eiwgdD10KQogIHRtcCA9IHN1cHByZXNzTWVzc2FnZXMoZ2FtKHogfiBucyh0LCBkZiA9IDMpLCBkYXRhPWQpKQogIAogICMgYmlucyBmb3IgbW9kZWwgZml0dGluZwogIGJiID0gc2VxKG1pbih0KSwgbWF4KHQpLCBsZW5ndGgub3V0ID0gMTAwKQogIGdlbmVfZml0X3ZhbHNbLGddID0gc3VwcHJlc3NNZXNzYWdlcyhwcmVkaWN0KHRtcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZGF0YS5mcmFtZSh0ID0gYmIpKSkKICBwID0gc3VtbWFyeSh0bXApJHBhcmFtZXRyaWMuYW5vdmEkYFByKD5GKWBbMV0KICBnZW5lX2ZpdF9wID0gYyhnZW5lX2ZpdF9wLCBwKQogIG5hbWVzKGdlbmVfZml0X3ApW2xlbmd0aChnZW5lX2ZpdF9wKV0gPSBnCn0KCmdlbmVfZml0X3AgPSBmZHJ0b29sOjpmZHJ0b29sKGdlbmVfZml0X3AsIHN0YXRpc3RpYz0icHZhbHVlIiwgcGxvdD1GLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1GLCBjdXRvZmYubWV0aG9kPSJwY3QwIiwgcGN0MD0wLjkpJHF2YWwKYGBgCgoKIyMgQW5hbHlzaXMgb2Ygb3RoZXIgY29uZGl0aW9ucwpGaXJzdCwgY2xlYW4gdGhlIGRhdGEgZnJvbSB0aGUgb3RoZXIgdHdvIGNvbmRpdGlvbnMKCmBgYHtyfQplbmRfY2VsbHMkZW1ib2xpc2VkID0gRmluZE5laWdoYm9ycyhlbmRfY2VsbHMkZW1ib2xpc2VkLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcmNlLnJlY2FsYyA9IFQsIGdyYXBoLm5hbWUgPSAiZW5kbyIpCmVuZF9jZWxscyRlbWJvbGlzZWQgPSBGaW5kQ2x1c3RlcnMoZW5kX2NlbGxzJGVtYm9saXNlZCwgZ3JhcGgubmFtZSA9ICJlbmRvIiwgYWxnb3JpdGhtID0gMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x1dGlvbiA9IDAuOCwgdmVyYm9zZSA9IEYpCkRpbVBsb3QoZW5kX2NlbGxzJGVtYm9saXNlZCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJlbmRvX3Jlcy4wLjgiKQplbmRfY2VsbHMkZW1ib2xpc2VkID0gU2V0SWRlbnQoZW5kX2NlbGxzJGVtYm9saXNlZCwgdmFsdWUgPSAiZW5kb19yZXMuMC44IikKbWtfZW1iID0gRmluZEFsbE1hcmtlcnMoZW5kX2NlbGxzJGVtYm9saXNlZCwgYXNzYXkgPSAiU0NUIikKCm91dGNlbGxzID0gKGVuZF9jZWxscyRlbWJvbGlzZWRAcmVkdWN0aW9ucyR1bWFwQGNlbGwuZW1iZWRkaW5nc1ssMl0+Mi4zICYgCiAgICAgICAgICAgICAgZW5kX2NlbGxzJGVtYm9saXNlZEByZWR1Y3Rpb25zJHVtYXBAY2VsbC5lbWJlZGRpbmdzWywxXTwxKSAjIHRoaXMgcmVtb3ZlcyB3aGF0IHNlZW0gdG8gYmUgY2VudHJhbCBoZXBhdG9jeXRlcwplbmRfY2VsbHMkZW1ib2xpc2VkID0gZW5kX2NlbGxzJGVtYm9saXNlZFssIW91dGNlbGxzXQplbmRfY2VsbHMkZW1ib2xpc2VkID0gZW5kX2NlbGxzJGVtYm9saXNlZFssZW5kX2NlbGxzJGVtYm9saXNlZEBtZXRhLmRhdGFbLCJlbmRvX3Jlcy4wLjgiXSE9MTJdICMgcmVtb3ZlIHRoZXNlIGV4dHJhIHN0ZWxsYXRlIGNlbGxzCgplbmRfY2VsbHMkZW1ib2xpc2VkID0gc3VwcHJlc3NXYXJuaW5ncyhTQ1RyYW5zZm9ybShlbmRfY2VsbHMkZW1ib2xpc2VkLCBkby5jb3JyZWN0LnVtaSA9IFQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJzLnRvLnJlZ3Jlc3MgPSBjKCJ1bmlxdWVfbmFtZSIsIm5Db3VudF9STkEiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLmZlYXR1cmVzLnJ2LnRoID0gMSwgc2VlZC51c2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuLm9ubHkudmFyLmdlbmVzID0gRiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5uID0gTlVMTCkpCmVuZF9jZWxscyRlbWJvbGlzZWQgPSBSdW5QQ0EoZW5kX2NlbGxzJGVtYm9saXNlZCwgdmVyYm9zZSA9IEYpCmVuZF9jZWxscyRlbWJvbGlzZWQgPSBSdW5VTUFQKGVuZF9jZWxscyRlbWJvbGlzZWQsIGRpbXMgPSAxOjE1LCB2ZXJib3NlID0gRikKRGltUGxvdChlbmRfY2VsbHMkZW1ib2xpc2VkLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gInVuaXF1ZV9uYW1lIikKCgpvdXRjZWxscyA9IChlbmRfY2VsbHMkcmVnZW5lcmF0aW5nQHJlZHVjdGlvbnMkdW1hcEBjZWxsLmVtYmVkZGluZ3NbLDJdPjIuNyAmIAogICAgICAgICAgICAgIGVuZF9jZWxscyRyZWdlbmVyYXRpbmdAcmVkdWN0aW9ucyR1bWFwQGNlbGwuZW1iZWRkaW5nc1ssMV08KC0xKSkgIyB0aGlzIHJlbW92ZXMgd2hhdCBzZWVtIHRvIGJlIGNlbnRyYWwgaGVwYXRvY3l0ZXMKZW5kX2NlbGxzJHJlZ2VuZXJhdGluZyA9IGVuZF9jZWxscyRyZWdlbmVyYXRpbmdbLCFvdXRjZWxsc10KCmVuZF9jZWxscyRyZWdlbmVyYXRpbmcgPSBzdXBwcmVzc1dhcm5pbmdzKFNDVHJhbnNmb3JtKGVuZF9jZWxscyRyZWdlbmVyYXRpbmcsIGRvLmNvcnJlY3QudW1pID0gVCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnMudG8ucmVncmVzcyA9IGMoInVuaXF1ZV9uYW1lIiwibkNvdW50X1JOQSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMucnYudGggPSAxLCBzZWVkLnVzZSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4ub25seS52YXIuZ2VuZXMgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLmZlYXR1cmVzLm4gPSBOVUxMKSkKZW5kX2NlbGxzJHJlZ2VuZXJhdGluZyA9IFJ1blBDQShlbmRfY2VsbHMkcmVnZW5lcmF0aW5nLCB2ZXJib3NlID0gRikKZW5kX2NlbGxzJHJlZ2VuZXJhdGluZyA9IFJ1blVNQVAoZW5kX2NlbGxzJHJlZ2VuZXJhdGluZywgZGltcyA9IDE6MTUsIHZlcmJvc2UgPSBGKQpEaW1QbG90KGVuZF9jZWxscyRyZWdlbmVyYXRpbmcsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAidW5pcXVlX25hbWUiKQpgYGAKCk5vdyB1c2UgdGhlIHRvcCBnZW5lcyB0byBsZWFybiB0aGUgcHNldWRvem9uYXRpb24gY29vcmRpbmF0ZXMgYW5kIHByb2plY3QgdGhlIGVtYm9saXNlZCBhbmQgcmVnZW5lcmF0aW5nIGRhdGEKCmBgYHtyfQpzaWdfZ2VuZXMgPSBnZW5lX2ZpdF9wW2dlbmVfZml0X3A8PTAuMDVdCnRvcF9zaWdfZ2VuZXMgPSBuYW1lcyhzaWdfZ2VuZXNbb3JkZXIoc2lnX2dlbmVzLCBkZWNyZWFzaW5nID0gRildWzE6MTAwMF0pCnRvcF9zaWdfZ2VuZXMgPSB0b3Bfc2lnX2dlbmVzW3RvcF9zaWdfZ2VuZXMgJWluJSByb3duYW1lcyhlbmRfY2VsbHMkZW1ib2xpc2VkQGFzc2F5cyRTQ1RAZGF0YSkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcF9zaWdfZ2VuZXMgJWluJSAgcm93bmFtZXMoZW5kX2NlbGxzJHJlZ2VuZXJhdGluZ0Bhc3NheXMkU0NUQGRhdGEpXQoKZGF0YV9nYW1faCA9IGNiaW5kKGRhdGEuZnJhbWUoIkRQVCIgPSBkZl9kYyREUFQpLAogICAgICAgICAgICAgICAgICAgTWF0cml4Ojp0KGVuZF9jZWxscyRoZWFsdGh5QGFzc2F5cyRTQ1RAZGF0YVt0b3Bfc2lnX2dlbmVzLF0pKQpkYXRhX2dhbV9oJERQVCA9IHNjYWxlczo6cmVzY2FsZShyYW5rKC1kYXRhX2dhbV9oJERQVCksIGMoMC4wMDAwMSwgMC45OTk5OSkpCgpnYW1faGVhbHRoeSA9IGdhbTo6Z2FtKERQVCB+IC4sIGRhdGEgPSBkYXRhX2dhbV9oLCBmYW1pbHkgPSBtZ2N2OjpiZXRhcihsaW5rID0gImxvZ2l0IiwgZXBzID0gMC4wMDAwMSkpCgpoZWFfcHJlZCA9IHByZWRpY3QoZ2FtX2hlYWx0aHksCiAgICAgICAgICAgICAgICAgICBNYXRyaXg6OnQoZW5kX2NlbGxzJGhlYWx0aEBhc3NheXMkU0NUQGRhdGFbdG9wX3NpZ19nZW5lcyxdKSwgdHlwZT0gInJlc3BvbnNlIikKcmVnX3ByZWQgPSBwcmVkaWN0KGdhbV9oZWFsdGh5LAogICAgICAgICAgICAgICAgICAgTWF0cml4Ojp0KGVuZF9jZWxscyRyZWdlbmVyYXRpbmdAYXNzYXlzJFNDVEBkYXRhW3RvcF9zaWdfZ2VuZXMsXSksIHR5cGU9ICJyZXNwb25zZSIpCmVtYl9wcmVkID0gcHJlZGljdChnYW1faGVhbHRoeSwKICAgICAgICAgICAgICAgICAgIE1hdHJpeDo6dChlbmRfY2VsbHMkZW1ib2xpc2VkQGFzc2F5cyRTQ1RAZGF0YVt0b3Bfc2lnX2dlbmVzLF0pLCB0eXBlPSAicmVzcG9uc2UiKQoKcGxvdChkZW5zaXR5KGRhdGFfZ2FtX2gkRFBUKSwgY29sID0gImdyZWVuIiwgeWxpbSA9IGMoMCwyLjc1KSkKbGluZXMoZGVuc2l0eShyZWdfcHJlZCksIGNvbCA9ICJibHVlIikKbGluZXMoZGVuc2l0eShlbWJfcHJlZCksIGNvbCA9ICJyZWQiKQpsaW5lcyhkZW5zaXR5KGhlYV9wcmVkKSwgY29sID0gImdyZXk1MCIpCmxlZ2VuZCgtMC4wNzMsMzIsIGxlZ2VuZCA9IGMoImhlYWx0aHkgKERDMSkiLCAiaGVhbHRoeSAocHJlZCkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICJlbWJvbGlzZWQgKHByZWQpIiwgInJlZ2VuZXJhdGluZyAocHJlZCkiKSwKICAgICAgIGZpbGwgPSBjKCJncmVlbiIsICJncmV5NTAiLCAicmVkIiwgImJsdWUiKSkKCnBsb3RfZGYgPSBkYXRhLmZyYW1lKHZhbHMgPSBjKGRhdGFfZ2FtX2gkRFBULCBlbWJfcHJlZCwgcmVnX3ByZWQpLCAKICAgICAgICAgICAgICAgICAgICAgQ29uZGl0aW9uID0gYyhyZXAoImhlYWx0aHkiLCBsZW5ndGgoZGF0YV9nYW1faCREUFQpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCJlbWJvbGlzZWQiLCBsZW5ndGgoZW1iX3ByZWQpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCJyZWdlbmVyYXRpbmciLCBsZW5ndGgocmVnX3ByZWQpKSksCiAgICAgICAgICAgICAgICAgICAgIERvbm9yID0gYyhlbmRfY2VsbHMkaGVhbHRoeSREb25vciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmRfY2VsbHMkZW1ib2xpc2VkJERvbm9yLCBlbmRfY2VsbHMkcmVnZW5lcmF0aW5nJERvbm9yKSwKICAgICAgICAgICAgICAgICAgICAgY3QgPSBjKGVuZF9jZWxscyRoZWFsdGh5JGFsbGNlbGxzX3NpbXAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kX2NlbGxzJGVtYm9saXNlZCRhbGxjZWxsc19zaW1wLCBlbmRfY2VsbHMkcmVnZW5lcmF0aW5nJGFsbGNlbGxzX3NpbXApKQpwbG90X2RmJGJpbnMxMDAgPSBjdXQocGxvdF9kZiR2YWxzLCAxMCkKcGxvdF9kZiRDb25kaXRpb24gPSBmYWN0b3IocGxvdF9kZiRDb25kaXRpb24sIGxldmVscyA9IGMoImhlYWx0aHkiLCAiZW1ib2xpc2VkIiwgInJlZ2VuZXJhdGluZyIpKQpnZ3Bsb3QocGxvdF9kZiwgYWVzKHggPSBiaW5zMTAwLCBmaWxsID0gQ29uZGl0aW9uKSkrCiAgICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpKwogICAgIGxhYnMoeCA9ICJ6b25hdGlvbiAoYmlubmVkKSIsIHkgPSAicHJvcG9ydGlvbiIpKwogICAgIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsMCkpKwogICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbGNvbmQpKwogICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gInRvcCIpKSsKICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICBsZWdlbmQudGl0bGUuYWxpZ24gPSAwLAogICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpnZ3Bsb3QocGxvdF9kZiwgYWVzKHggPSBiaW5zMTAwLCBmaWxsID0gQ29uZGl0aW9uKSkrCiAgICAgZmFjZXRfd3JhcCh+Q29uZGl0aW9uKSsKICAgICBnZW9tX2JhcigpKwogICAgIGxhYnMoeCA9ICJ6b25hdGlvbiAoYmlubmVkKSIsIHkgPSAiTnVtYmVyIG9mIGNlbGxzIikrCiAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSkrCiAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sY29uZCkrCiAgICAgY29vcmRfZmxpcCgpKwogICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gInRvcCIpKSsKICAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICBsZWdlbmQudGl0bGUuYWxpZ24gPSAwLAogICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpnZ3Bsb3QocGxvdF9kZiwgYWVzKHggPSB2YWxzLCBjb2xvdXIgPSBEb25vcikpKwogICAgIGZhY2V0X3dyYXAofkNvbmRpdGlvbikrCiAgICAgZ2VvbV9kZW5zaXR5KCkrCiAgICAgbGFicyh4ID0gInpvbmF0aW9uIChiaW5uZWQpIiwgeSA9ICJwcm9wb3J0aW9uIikrCiAgICAgdGhlbWVfYncoKSsKICAgICB0aGVtZShsZWdlbmQudGl0bGUuYWxpZ24gPSAwLAogICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpnZ3Bsb3QocGxvdF9kZiwgYWVzKHggPSB2YWxzLCBjb2xvdXIgPSBjdCkpKwogICAgIGZhY2V0X3dyYXAofkNvbmRpdGlvbikrCiAgICAgZ2VvbV9kZW5zaXR5KCkrCiAgICAgbGFicyh4ID0gInpvbmF0aW9uIChiaW5uZWQpIiwgeSA9ICJwcm9wb3J0aW9uIikrCiAgICAgdGhlbWVfYncoKSsKICAgICB0aGVtZShsZWdlbmQudGl0bGUuYWxpZ24gPSAwLAogICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKcmVnX2RmID0gZGF0YS5mcmFtZSgicHQiID0gcmVnX3ByZWQsICJkb24iID0gZW5kX2NlbGxzJHJlZ2VuZXJhdGluZyREb25vcikKcmVnX2RmID0gY2JpbmQocmVnX2RmLCBNYXRyaXg6OnQoZW5kX2NlbGxzJHJlZ2VuZXJhdGluZ0Bhc3NheXMkU0NUQGRhdGFbZ2ZyZXNoLF0pKQpnZ3Bsb3QocmVnX2RmLCBhZXMoeCA9IHB0LCB5ID0gQ0xFQzFCLCBjb2xvdXIgPSBkb24pKSsKICBnZW9tX3BvaW50KCkrCiAgdGhlbWVfY2xhc3NpYygpKwogIGZhY2V0X3dyYXAofmRvbikrCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMykrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygwLDEpKQoKZ2dwbG90KHJlZ19kZiwgYWVzKHggPSBDTEVDMUIsIHkgPSBMWVZFMSwgY29sb3VyID0gZG9uKSkrCiAgZ2VvbV9wb2ludCgpKwogIHRoZW1lX2NsYXNzaWMoKSsKICBmYWNldF93cmFwKH5kb24pCgp0cmFqX2xpc3QgPSBsaXN0KCJoZWFsdGh5IiA9IGRhdGFfZ2FtX2gkRFBULCAiZW1ib2xpc2VkIiA9IGVtYl9wcmVkLCAicmVnZW5lcmF0aW5nIiA9IHJlZ19wcmVkKQpgYGAKCkNhbGN1bGF0ZSBjb3JyZWxhdGlvbnMKCmBgYHtyfQpjb3JfaCA9IHQoY29yKGRhdGFfZ2FtX2gkRFBULCBhcy5tYXRyaXgoTWF0cml4Ojp0KGVuZF9jZWxscyRoZWFsdGh5QGFzc2F5cyRTQ1RAZGF0YSkpLAogICAgICAgICAgICAgIG1ldGhvZCA9ICJzcCIpKQpjb3JfZW1iID0gdChjb3IoZW1iX3ByZWQsIGFzLm1hdHJpeChNYXRyaXg6OnQoZW5kX2NlbGxzJGVtYm9saXNlZEBhc3NheXMkU0NUQGRhdGEpKSwKICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJzcCIpKQpjb3JfcmVnID0gdChjb3IocmVnX3ByZWQsIGFzLm1hdHJpeChNYXRyaXg6OnQoZW5kX2NlbGxzJHJlZ2VuZXJhdGluZ0Bhc3NheXMkU0NUQGRhdGEpKSwKICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJzcCIpKQoKbCA9IGxpc3QoY29yX2gsIGNvcl9lbWIsIGNvcl9yZWcpCmFsbF9jb3JyID0gUmVkdWNlKGZ1bmN0aW9uKHgsIHkpIG1lcmdlKHgseSwgYnkgPSAicm4iLCBhbGwgPSBUKSwgCiAgICAgICAgICAgICAgICAgIGxhcHBseShsLCBmdW5jdGlvbih4KSBkYXRhLmZyYW1lKHgsIHJuID0gcm93Lm5hbWVzKHgpKSkpCmNvbG5hbWVzKGFsbF9jb3JyKSA9IGMoImdlbmVzIiwgImhlYWx0aHkiLCAiZW1ib2xpc2VkIiwgInJlZ2VuZXJhdGluZyIpCgp3cml0ZS5jc3YoYWxsX2NvcnIsIGZpbGUgPSBwYXN0ZTAoInJlc3VsdHMvem9uYXRpb25fY29uZC9lbmRfY29ycmVsYXRpb25zX3pvbmF0aW9uX3JhbmsuY3N2IiksCiAgICAgICAgICBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYpCmBgYAoKRmluZCB2YXJ5aW5nIGdlbmVzIC0gdXNpbmcgYWxsIGNlbGxzIGFsb25nIHRoZSByYW5rZWQgcHNldWRvdGltZQoKYGBge3J9CnF2YWxzX2xpc3QgPSBsaXN0KCkKZml0c19saXN0ID0gbGlzdCgpCgojIGdldCBIVkcgZnJvbSBhbGwgY29uZGl0aW9ucwpodmcgPSB1bmlxdWUoYyhlbmRfY2VsbHNbWyJoZWFsdGh5Il1dQGFzc2F5cyRTQ1RAdmFyLmZlYXR1cmVzLAogICAgICAgICAgICAgICBlbmRfY2VsbHNbWyJlbWJvbGlzZWQiXV1AYXNzYXlzJFNDVEB2YXIuZmVhdHVyZXMsCiAgICAgICAgICAgICAgIGVuZF9jZWxsc1tbInJlZ2VuZXJhdGluZyJdXUBhc3NheXMkU0NUQHZhci5mZWF0dXJlcykpCmh2ZyA9IGh2Z1todmcgJWluJSByb3duYW1lcyhlbmRfY2VsbHNbWyJoZWFsdGh5Il1dQGFzc2F5cyRTQ1RAZGF0YSkgJgogICAgICAgICAgICBodmcgJWluJSByb3duYW1lcyhlbmRfY2VsbHNbWyJlbWJvbGlzZWQiXV1AYXNzYXlzJFNDVEBkYXRhKSAmCiAgICAgICAgICAgIGh2ZyAlaW4lIHJvd25hbWVzKGVuZF9jZWxsc1tbInJlZ2VuZXJhdGluZyJdXUBhc3NheXMkU0NUQGRhdGEpXQoKZm9yKGNvbmQgaW4gbmFtZXMoZW5kX2NlbGxzKSl7CiAgcHJpbnQoY29uZCkKICAjIEZpdCBHQU0gZm9yIGVhY2ggZ2VuZSB1c2luZyBwc2V1ZG90aW1lIGFzIGluZGVwZW5kZW50IHZhcmlhYmxlLgogIHQgPSB0cmFqX2xpc3RbW2NvbmRdXQogIGdlbmVfZml0X3AgPSBjKCkKICBnZW5lX2ZpdF92YWxzID0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSAxOjEwMCkKICBmb3IoaSBpbiAxOmxlbmd0aChodmcpKXsKICAgIGcgPSBodmdbaV0KICAgIHogPSBlbmRfY2VsbHNbW2NvbmRdXUBhc3NheXMkU0NUQGRhdGFbZyxdCiAgICAKICAgIGQgPSBkYXRhLmZyYW1lKHo9eiwgdD10KQogICAgdG1wID0gc3VwcHJlc3NNZXNzYWdlcyhnYW0oeiB+IG5zKHQsIGRmID0gNCksIGRhdGE9ZCkpCiAgICAKICAgICMgYmlucyBmb3IgbW9kZWwgZml0dGluZwogICAgYmIgPSBzZXEobWluKHQpLCBtYXgodCksIGxlbmd0aC5vdXQgPSAxMDApCiAgICBnZW5lX2ZpdF92YWxzWyxnXSA9IHN1cHByZXNzTWVzc2FnZXMocHJlZGljdCh0bXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZGF0YS5mcmFtZSh0ID0gYmIpKSkKICAgIHAgPSBzdW1tYXJ5KHRtcCkkcGFyYW1ldHJpYy5hbm92YSRgUHIoPkYpYFsxXQogICAgZ2VuZV9maXRfcCA9IGMoZ2VuZV9maXRfcCwgcCkKICAgIG5hbWVzKGdlbmVfZml0X3ApW2xlbmd0aChnZW5lX2ZpdF9wKV0gPSBnCiAgfQogIAogIGlmKHN1bShpcy5uYShnZW5lX2ZpdF9wKSk+MCl7CiAgICBnZW5lX2ZpdF9wW2lzLm5hKGdlbmVfZml0X3ApXSA9IDEKICB9CiAgZ2VuZV9maXRfcCA9IGZkcnRvb2w6OmZkcnRvb2woZ2VuZV9maXRfcCwgc3RhdGlzdGljPSJwdmFsdWUiLCBwbG90PUYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2U9RiwgY3V0b2ZmLm1ldGhvZD0icGN0MCIsIHBjdDA9MC45KSRxdmFsCiAgCiAgcXZhbHNfbGlzdFtbY29uZF1dID0gZ2VuZV9maXRfcAogIGZpdHNfbGlzdFtbY29uZF1dID0gZ2VuZV9maXRfdmFscwp9CmBgYAoKU2F2ZSB0cmFqZWN0b3JpZXMgYW5kIGdlbmVzCgpgYGB7cn0Kc2F2ZShmaXRzX2xpc3QsIHF2YWxzX2xpc3QsIHRyYWpfbGlzdCwgCiAgICAgZmlsZSA9ICJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvZW5kX3RyYWpfcXZhbF9maXRzX3JhbmsuUkRhdGEiKQpgYGAKCkZpbmQgdmFyeWluZyBnZW5lcyAtIG5vcm1hbGlzZWQgYnkgYmlucyBhbG9uZyB0aGUgcmFua2VkIHBzZXVkb3RpbWUKCmBgYHtyfQpxdmFsc19iX2xpc3QgPSBsaXN0KCkKZml0c19iX2xpc3QgPSBsaXN0KCkKCiMgZ2V0IEhWRyBmcm9tIGFsbCBjb25kaXRpb25zCmh2ZyA9IHVuaXF1ZShjKGVuZF9jZWxsc1tbImhlYWx0aHkiXV1AYXNzYXlzJFNDVEB2YXIuZmVhdHVyZXMsCiAgICAgICAgICAgICAgIGVuZF9jZWxsc1tbImVtYm9saXNlZCJdXUBhc3NheXMkU0NUQHZhci5mZWF0dXJlcywKICAgICAgICAgICAgICAgZW5kX2NlbGxzW1sicmVnZW5lcmF0aW5nIl1dQGFzc2F5cyRTQ1RAdmFyLmZlYXR1cmVzKSkKaHZnID0gaHZnW2h2ZyAlaW4lIHJvd25hbWVzKGVuZF9jZWxsc1tbImhlYWx0aHkiXV1AYXNzYXlzJFNDVEBkYXRhKSAmCiAgICAgICAgICAgIGh2ZyAlaW4lIHJvd25hbWVzKGVuZF9jZWxsc1tbImVtYm9saXNlZCJdXUBhc3NheXMkU0NUQGRhdGEpICYKICAgICAgICAgICAgaHZnICVpbiUgcm93bmFtZXMoZW5kX2NlbGxzW1sicmVnZW5lcmF0aW5nIl1dQGFzc2F5cyRTQ1RAZGF0YSldCgpmb3IoY29uZCBpbiBuYW1lcyhlbmRfY2VsbHMpKXsKICBwcmludChjb25kKQogIHNldC5zZWVkKDEpCiAgIyBmaWx0ZXJpbmcgdGhlIGNlbGxzIGJ5IGJpbnMKICBiaW5zID0gY3V0KHRyYWpfbGlzdFtbY29uZF1dLCAxMDApCiAgZG9ucyA9IGVuZF9jZWxsc1tbY29uZF1dJERvbm9yCiAgbmRvbnMgPSByb3dTdW1zKHRhYmxlKGJpbnMsIGRvbnMpPjEpCiAgbmNlbGxzID0gcm93U3Vtcyh0YWJsZShiaW5zLCBkb25zKSkKICBzYW1wbGVpZiA9IGZ1bmN0aW9uKGNlbGxzLCBiaW5zLCBkb25zKXsKICAgIHJlcyA9IGMoKQogICAgZm9yKGQgaW4gdW5pcXVlKGRvbnMpKXsKICAgICAgZm9yKGIgaW4gdW5pcXVlKGJpbnMpKXsKICAgICAgICB4ID0gY2VsbHNbYmlucz09YiAmIGRvbnM9PWRdCiAgICAgICAgaWYobGVuZ3RoKHgpPj0zMCl7CiAgICAgICAgICByZXMgPSBjKHJlcywgc2FtcGxlKHgsIDMwLCByZXBsYWNlID0gRikpCiAgICAgICAgfSBlbHNlIGlmKGxlbmd0aCh4KT49MSAmIG5kb25zW2JdPj0yICYgbmNlbGxzW2JdPj0xMCl7CiAgICAgICAgICByZXMgPSBjKHJlcywgeCkKICAgICAgICB9IGVsc2V7CiAgICAgICAgICByZXMgPSBjKHJlcywgTlVMTCkKICAgICAgICB9CiAgICAgIH0KICAgIH0KICAgIHJldHVybihyZXMpCiAgfQogIHdoaWNoY2VsbHMgPSBzYW1wbGVpZigxOmxlbmd0aChiaW5zKSwgYmlucywgZG9ucykKICAKICAjIEZpdCBHQU0gZm9yIGVhY2ggZ2VuZSB1c2luZyBwc2V1ZG90aW1lIGFzIGluZGVwZW5kZW50IHZhcmlhYmxlLgogIHQgPSB0cmFqX2xpc3RbW2NvbmRdXVt3aGljaGNlbGxzXQogIGdlbmVfZml0X3AgPSBjKCkKICBnZW5lX2ZpdF92YWxzID0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSAxOjEwMCkKICBmb3IoaSBpbiAxOmxlbmd0aChodmcpKXsKICAgIGcgPSBodmdbaV0KICAgIHogPSBlbmRfY2VsbHNbW2NvbmRdXUBhc3NheXMkU0NUQGRhdGFbZyx3aGljaGNlbGxzXQogICAgCiAgICBkID0gZGF0YS5mcmFtZSh6PXosIHQ9dCkKICAgIHRtcCA9IHN1cHByZXNzTWVzc2FnZXMoZ2FtKHogfiBucyh0LCBkZiA9IDMpLCBkYXRhPWQpKQogICAgCiAgICAjIGJpbnMgZm9yIG1vZGVsIGZpdHRpbmcKICAgIGJiID0gc2VxKG1pbih0KSwgbWF4KHQpLCBsZW5ndGgub3V0ID0gMTAwKQogICAgZ2VuZV9maXRfdmFsc1ssZ10gPSBzdXBwcmVzc01lc3NhZ2VzKHByZWRpY3QodG1wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGRhdGEuZnJhbWUodCA9IGJiKSkpCiAgICBwID0gc3VtbWFyeSh0bXApJHBhcmFtZXRyaWMuYW5vdmEkYFByKD5GKWBbMV0KICAgIGdlbmVfZml0X3AgPSBjKGdlbmVfZml0X3AsIHApCiAgICBuYW1lcyhnZW5lX2ZpdF9wKVtsZW5ndGgoZ2VuZV9maXRfcCldID0gZwogIH0KICAKICBpZihzdW0oaXMubmEoZ2VuZV9maXRfcCkpPjApewogICAgZ2VuZV9maXRfcFtpcy5uYShnZW5lX2ZpdF9wKV0gPSAxCiAgfQogIGdlbmVfZml0X3AgPSBmZHJ0b29sOjpmZHJ0b29sKGdlbmVfZml0X3AsIHN0YXRpc3RpYz0icHZhbHVlIiwgcGxvdD1GLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlPUYsIGN1dG9mZi5tZXRob2Q9InBjdDAiLCBwY3QwPTAuOSkkcXZhbAogIAogIHF2YWxzX2JfbGlzdFtbY29uZF1dID0gZ2VuZV9maXRfcAogIGZpdHNfYl9saXN0W1tjb25kXV0gPSBnZW5lX2ZpdF92YWxzCn0KYGBgCgpTYXZlIHRyYWplY3RvcmllcyBhbmQgZ2VuZXMKCmBgYHtyfQpzYXZlKGZpdHNfYl9saXN0LCBxdmFsc19iX2xpc3QsIHRyYWpfbGlzdCwgCiAgICAgZmlsZSA9ICJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvZW5kX2Jpbm5lZF90cmFqX3F2YWxfZml0c19yYW5rLlJEYXRhIikKYGBgCgpGaW5kIHZhcnlpbmcgZ2VuZXMgLSB1c2luZyBlYWNoIGRvbm9yIGFzIGEgcmVwbGljYXRlIGFuZCBjYWxjdWxhdGluZyB0aGUgYXZlcmFnZSBleHByZXNzaW9uIGZvciBlYWNoIG9mIDEwMCBiaW5zCgpgYGB7cn0KcXZhbHNfZGJfbGlzdCA9IGxpc3QoKQpmaXRzX2RiX2xpc3QgPSBsaXN0KCkKCiMgZ2V0IEhWRyBmcm9tIGFsbCBjb25kaXRpb25zCmh2ZyA9IHVuaXF1ZShjKGVuZF9jZWxsc1tbImhlYWx0aHkiXV1AYXNzYXlzJFNDVEB2YXIuZmVhdHVyZXMsCiAgICAgICAgICAgICAgIGVuZF9jZWxsc1tbImVtYm9saXNlZCJdXUBhc3NheXMkU0NUQHZhci5mZWF0dXJlcywKICAgICAgICAgICAgICAgZW5kX2NlbGxzW1sicmVnZW5lcmF0aW5nIl1dQGFzc2F5cyRTQ1RAdmFyLmZlYXR1cmVzKSkKaHZnID0gaHZnW2h2ZyAlaW4lIHJvd25hbWVzKGVuZF9jZWxsc1tbImhlYWx0aHkiXV1AYXNzYXlzJFNDVEBkYXRhKSAmCiAgICAgICAgICAgIGh2ZyAlaW4lIHJvd25hbWVzKGVuZF9jZWxsc1tbImVtYm9saXNlZCJdXUBhc3NheXMkU0NUQGRhdGEpICYKICAgICAgICAgICAgaHZnICVpbiUgcm93bmFtZXMoZW5kX2NlbGxzW1sicmVnZW5lcmF0aW5nIl1dQGFzc2F5cyRTQ1RAZGF0YSldCgptZXRhX2RmID0gZGF0YS5mcmFtZSgicHQiID0gYyh0cmFqX2xpc3QkaGVhbHRoeSwgdHJhal9saXN0JGVtYm9saXNlZCwgdHJhal9saXN0JHJlZ2VuZXJhdGluZyksCiAgICAgICAgICAgICAgICAgICAgICJkb25vcnMiID0gYyhlbmRfY2VsbHMkaGVhbHRoeSREb25vciwgZW5kX2NlbGxzJGVtYm9saXNlZCREb25vciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZF9jZWxscyRyZWdlbmVyYXRpbmckRG9ub3IpLAogICAgICAgICAgICAgICAgICAgICAiY29uZCIgPSBjKGVuZF9jZWxscyRoZWFsdGh5JENvbmRpdGlvbiwgZW5kX2NlbGxzJGVtYm9saXNlZCRDb25kaXRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmRfY2VsbHMkcmVnZW5lcmF0aW5nJENvbmRpdGlvbikpCm1ldGFfZGYkYmlucyA9IGN1dChtZXRhX2RmJHB0LCAxMDApCgpmb3IoY29uZCBpbiB1bmlxdWUobWV0YV9kZiRjb25kKSl7CiAgcHJpbnQoY29uZCkKICBzZXQuc2VlZCgxKQogIAogICMgRml0IEdBTSBmb3IgZWFjaCBnZW5lCiAgdCA9IDE6MTAwCiAgc3ViX21ldGFfZGYgPSBtZXRhX2RmW21ldGFfZGYkY29uZD09Y29uZCxdCiAgZ2VuZV9maXRfcCA9IGMoKQogIGdlbmVfZml0X3ZhbHMgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IDE6MTAwKQogIGZvcihpIGluIDE6bGVuZ3RoKGh2ZykpewogICAgZyA9IGh2Z1tpXQogICAgeiA9IGVuZF9jZWxsc1tbY29uZF1dQGFzc2F5cyRTQ1RAZGF0YVtnLF0KICAgIAogICAgZCA9IGFnZ3JlZ2F0ZSh6fmJpbnMrZG9ub3JzLCBkYXRhID0gY2JpbmQoc3ViX21ldGFfZGYsIHopLCBGVU49Im1lYW4iKQogICAgY2MgPSBkYXRhLmZyYW1lKHRhYmxlKHN1Yl9tZXRhX2RmJGJpbnMsIGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIoc3ViX21ldGFfZGYkZG9ub3JzKSkpKQogICAgY2MgPSBjY1tjYyRGcmVxPT0wLF0KICAgIGNvbG5hbWVzKGNjKSA9IGNvbG5hbWVzKGQpCiAgICBkID0gcmJpbmQoZCwgY2MpCiAgICBkID0gZFtvcmRlcihkJGJpbnMsIGRlY3JlYXNpbmcgPSBGKSxdCiAgICBkJHQgPSByZXAodCwgZWFjaCA9IGxlbmd0aCh1bmlxdWUoZCRkb25vcnMpKSkKICAgIHRtcCA9IHN1cHByZXNzTWVzc2FnZXMoZ2FtKHogfiBucyh0LCBkZiA9IDMpLCBkYXRhPWQpKQogICAgCiAgICAjIGJpbnMgZm9yIG1vZGVsIGZpdHRpbmcKICAgIGJiID0gc2VxKG1pbih0KSwgbWF4KHQpLCBsZW5ndGgub3V0ID0gMTAwKQogICAgZ2VuZV9maXRfdmFsc1ssZ10gPSBzdXBwcmVzc01lc3NhZ2VzKHByZWRpY3QodG1wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGRhdGEuZnJhbWUodCA9IGJiKSkpCiAgICBwID0gc3VtbWFyeSh0bXApJHBhcmFtZXRyaWMuYW5vdmEkYFByKD5GKWBbMV0KICAgIGdlbmVfZml0X3AgPSBjKGdlbmVfZml0X3AsIHApCiAgICBuYW1lcyhnZW5lX2ZpdF9wKVtsZW5ndGgoZ2VuZV9maXRfcCldID0gZwogIH0KICAKICBpZihzdW0oaXMubmEoZ2VuZV9maXRfcCkpPjApewogICAgZ2VuZV9maXRfcFtpcy5uYShnZW5lX2ZpdF9wKV0gPSAxCiAgfQogIGdlbmVfZml0X3AgPSBmZHJ0b29sOjpmZHJ0b29sKGdlbmVfZml0X3AsIHN0YXRpc3RpYz0icHZhbHVlIiwgcGxvdD1GLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlPUYsIGN1dG9mZi5tZXRob2Q9InBjdDAiLCBwY3QwPTAuOSkkcXZhbAogIAogIHF2YWxzX2RiX2xpc3RbW2NvbmRdXSA9IGdlbmVfZml0X3AKICBmaXRzX2RiX2xpc3RbW2NvbmRdXSA9IGdlbmVfZml0X3ZhbHMKfQpgYGAKClNhdmUgdHJhamVjdG9yaWVzIGFuZCBnZW5lcwoKYGBge3J9CnNhdmUoZml0c19kYl9saXN0LCBxdmFsc19kYl9saXN0LCB0cmFqX2xpc3QsIAogICAgIGZpbGUgPSAicmVzdWx0cy96b25hdGlvbl9jb25kL2VuZF9iaW5uZWREb25vcl90cmFqX3F2YWxfZml0c19yYW5rLlJEYXRhIikKYGBgCgpQbG90IGNlbGwgZGlzdHJpYnV0aW9ucyBpbiBwc2V1ZG90aW1lCgpgYGB7cn0KcGxvdF9kZiA9IGRhdGEuZnJhbWUoInBzZXVkb3RpbWUiID0gYyh0cmFqX2xpc3QkaGVhbHRoeSwgdHJhal9saXN0JGVtYm9saXNlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFqX2xpc3QkcmVnZW5lcmF0aW5nKSwKICAgICAgICAgICAiZG9ub3IiID0gYyhlbmRfY2VsbHMkaGVhbHRoeUBtZXRhLmRhdGFbLCJEb25vciJdLAogICAgICAgICAgICAgICAgICAgICAgIGVuZF9jZWxscyRlbWJvbGlzZWRAbWV0YS5kYXRhW25hbWVzKHRyYWpfbGlzdCRlbWJvbGlzZWQpLCJEb25vciJdLAogICAgICAgICAgICAgICAgICAgICAgIGVuZF9jZWxscyRyZWdlbmVyYXRpbmdAbWV0YS5kYXRhW25hbWVzKHRyYWpfbGlzdCRyZWdlbmVyYXRpbmcpLCJEb25vciJdKSwKICAgICAgICAgICAiY29uZCIgPSBjKHJlcCgiaGVhbHRoeSIsIGxlbmd0aCh0cmFqX2xpc3QkaGVhbHRoeSkpLCAKICAgICAgICAgICAgICAgICAgICAgIHJlcCgiZW1ib2xpc2VkIiwgbGVuZ3RoKHRyYWpfbGlzdCRlbWJvbGlzZWQpKSwKICAgICAgICAgICAgICAgICAgICAgIHJlcCgicmVnZW5lcmF0aW5nIiwgbGVuZ3RoKHRyYWpfbGlzdCRyZWdlbmVyYXRpbmcpKSkpCgpwZGYoInJlc3VsdHMvem9uYXRpb25fY29uZC9lbmRfY2VsbF9kaXN0cmlidXRpb25zX3pvbmF0aW9uLnBkZiIsIHVzZURpbmdiYXRzID0gRiwgCiAgICB3aWR0aCA9IDYsIGhlaWdodCA9IDUpCmdncGxvdChwbG90X2RmLCBhZXMoeCA9IGRvbm9yLCB5ID0gcHNldWRvdGltZSwgZmlsbCA9IGNvbmQpKSsKICBnZW9tX2JveHBsb3Qobm90Y2ggPSBUKSsKICB0aGVtZV9idygpCgpnZ3Bsb3QocGxvdF9kZiwgYWVzKHggPSBwc2V1ZG90aW1lLCBncm91cCA9IGRvbm9yLCBjb2xvdXIgPSBkb25vcikpKwogIGZhY2V0X3dyYXAofmNvbmQpKwogIGdlb21fZGVuc2l0eSgpKwogIHRoZW1lX2J3KCkKCmdncGxvdChwbG90X2RmLCBhZXMoeCA9IHBzZXVkb3RpbWUsIGNvbG91ciA9IGNvbmQpKSsKICBnZW9tX2RlbnNpdHkoKSsKICB0aGVtZV9idygpCmRldi5vZmYoKQpgYGAKCkRvIGNyb3NzIHZhbGlkYXRpb24gb24gdGhlIGhlYWx0aHkgdHJhamVjdG9yeQoKYGBge3J9CnNpZ19nZW5lcyA9IHF2YWxzX2JfbGlzdCRoZWFsdGh5W3F2YWxzX2JfbGlzdCRoZWFsdGh5PD0wLjA1XQp0b3Bfc2lnX2dlbmVzID0gbmFtZXMoc2lnX2dlbmVzW29yZGVyKHNpZ19nZW5lcywgZGVjcmVhc2luZyA9IEYpXVsxOjEwMDBdKSAKIyA1MDAgaW4gdGhpcyBjYXNlIGFjdHVhbGx5IHNlZW1zIHRvIGJlIG9uZSBvZiB0aGUgbW9kZWxzIHdpdGggdGhlIGxvd2VzdCBNU0UKCmRhdGFfZ2FtX2ggPSBjYmluZChkYXRhLmZyYW1lKCJEUFQiID0gdHJhal9saXN0JGhlYWx0aHkpLAogICAgICAgICAgICAgICAgICAgTWF0cml4Ojp0KGVuZF9jZWxscyRoZWFsdGh5QGFzc2F5cyRTQ1RAZGF0YVt0b3Bfc2lnX2dlbmVzLF0pKQpjb2xuYW1lcyhkYXRhX2dhbV9oKSA9IGdzdWIoIi0iLCAiXyIsIGNvbG5hbWVzKGRhdGFfZ2FtX2gpLCBmaXhlZCA9IFQpCmNvbG5hbWVzKGRhdGFfZ2FtX2gpID0gZ3N1YigiLiIsICJfIiwgY29sbmFtZXMoZGF0YV9nYW1faCksIGZpeGVkID0gVCkKCmN2X2dhbSA9IENWZ2FtKGRhdGEgPSBkYXRhX2dhbV9oLCBmb3JtdWxhID0gRFBUIH4gLiwgbmZvbGQgPSAxMCwgc2VlZCA9IDEsIG1ldGhvZCA9ICJnbG0uZml0IikKCnBkZigicmVzdWx0cy96b25hdGlvbl9oZWFsdGh5L2VuZF9vcmlnaW5hbF9maXR0ZWRfcHNldWRvdGltZS5wZGYiLCB1c2VEaW5nYmF0cyA9IEYsIAogICAgd2lkdGggPSA2LCBoZWlnaHQgPSA1KQpwbG90KGRhdGFfZ2FtX2gkRFBULCBjdl9nYW0kZml0dGVkLCB4bGFiID0gIkhlYWx0aHkgcHNldWRvdGltZSAoRFBUMzYzKSIseWxhYiA9ICJQcmVkaWN0ZWQgSGVhbHRoeSIsCiAgICAgbWFpbiA9IHBhc3RlMCgiSGVhbHRoeSBvcmlnaW5hbCB2cyBmaXR0ZWQgcHNldWRvdGltZVxuTVNFOiAiLCByb3VuZChjdl9nYW0kY3ZzY2FsZSwgNikpLAogICAgIHBjaCA9IDE5LCBjZXggPSAwLjUpCmFibGluZSgwLDEsIGNvbCA9ICJibHVlIiwgbHdkID0gMykKZGV2Lm9mZigpCmBgYAoKU2F2ZSB6b25hdGlvbiByZXN1bHRzCgpgYGB7cn0KIyBkZWZpbmUgdGhlIGludGVydmFscyBiYXNlZCBvbiBoZWFsdGh5CmRmX2ggPSBkYXRhLnRhYmxlKCJ6b25hdGlvbl9wdCIgPSB0cmFqX2xpc3QkaGVhbHRoeSkKZGZfaCR6b25hdGlvbl9pbnQgPSBjdXQoZGZfaCR6b25hdGlvbl9wdCwgMykKCmRmX2UgPSBkYXRhLnRhYmxlKCJ6b25hdGlvbl9wdCIgPSB0cmFqX2xpc3QkZW1ib2xpc2VkKQpkZl9lID0gZGZfaFtkZl9lLCBvbj0iem9uYXRpb25fcHQiLCByb2xsPUluZiwgcm9sbGVuZHMgPSBUXQoKZGZfciA9IGRhdGEudGFibGUoInpvbmF0aW9uX3B0IiA9IHRyYWpfbGlzdCRyZWdlbmVyYXRpbmcpCmRmX3IgPSBkZl9oW2RmX3IsIG9uPSJ6b25hdGlvbl9wdCIsIHJvbGw9SW5mLCByb2xsZW5kcyA9IFRdCgpsaXN0X2RmID0gbGlzdCgiaGVhbHRoeSIgPSBkZl9oLCAiZW1ib2xpc2VkIiA9IGRmX2UsICJyZWdlbmVyYXRpbmciID0gZGZfcikKCmZvcihuIGluIG5hbWVzKHRyYWpfbGlzdCkpewogIHB0ZGYgPSBsaXN0X2RmW1tuXV0KICByb3duYW1lcyhwdGRmKSA9IGNvbG5hbWVzKGVuZF9jZWxsc1tbbl1dKQogIGVuZF9jZWxsc1tbbl1dID0gQWRkTWV0YURhdGEoZW5kX2NlbGxzW1tuXV0sIG1ldGFkYXRhID0gcHRkZikKICB3cml0ZS5jc3YoZW5kX2NlbGxzW1tuXV1AbWV0YS5kYXRhWyxjKCJ1bmlxdWVfbmFtZSIsICJDb25kaXRpb24iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ6b25hdGlvbl9wdCIsICJ6b25hdGlvbl9pbnQiKV0sCiAgICAgICAgICAgIGZpbGUgPSBwYXN0ZTAoInJlc3VsdHMvem9uYXRpb25fY29uZC9lbmRfem9uYXRpb25fIiwgbiwgIl9yYW5rLmNzdiIpLAogICAgICAgICAgICBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBULCBxdW90ZSA9IEYpCn0KCnNhdmVSRFMoZW5kX2NlbGxzLCBmaWxlID0gInJlc3VsdHMvem9uYXRpb25fY29uZC9lbmRfY2VsbHNfem9uYXRpb25fcmFuay5SRFMiKQpgYGAKCgoKIyBDb21wYXJpbmcgZ2VuZXMgYmV0d2VlbiBjb25kaXRpb25zCkxvYWQgZGF0YQoKYGBge3J9CmVuZF9jZWxscyA9IHJlYWRSRFMoZmlsZSA9ICJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvZW5kX2NlbGxzX3pvbmF0aW9uX3JhbmsuUkRTIikKaGVwX2NlbGxzID0gcmVhZFJEUyhmaWxlID0gInJlc3VsdHMvem9uYXRpb25fY29uZC9oZXBfY2VsbHNfem9uYXRpb25fcmFuay5SRFMiKQpgYGAKCkxvYWQgZml0cwoKYGBge3J9CmxvYWQoInJlc3VsdHMvem9uYXRpb25fY29uZC9oZXBfYmlubmVkRG9ub3JfdHJhal9xdmFsX2ZpdHNfcmFuay5SRGF0YSIpCmhlcF9maXRzX2xpc3QgPSBmaXRzX2RiX2xpc3QKaGVwX3F2YWxzX2xpc3QgPSBxdmFsc19kYl9saXN0CmhlcF90cmFqID0gdHJhal9saXN0CmxvYWQoInJlc3VsdHMvem9uYXRpb25fY29uZC9lbmRfYmlubmVkRG9ub3JfdHJhal9xdmFsX2ZpdHNfcmFuay5SRGF0YSIpCmVuZF9maXRzX2xpc3QgPSBmaXRzX2RiX2xpc3QKZW5kX3F2YWxzX2xpc3QgPSBxdmFsc19kYl9saXN0CmVuZF90cmFqID0gdHJhal9saXN0CmBgYAoKQ2FsY3VsYXRlIGV4cHJlc3Npb24gcGVyIGJpbnMKCmBgYHtyfQpuYmlucyA9IDEwMAoKZGZoZXBfaCA9IGRhdGEudGFibGUoInpvbmF0aW9uX3B0IiA9IGhlcF90cmFqJGhlYWx0aHkpCmRmaGVwX2gkem9uYXRpb25faW50ID0gY3V0KGRmaGVwX2gkem9uYXRpb25fcHQsIG5iaW5zKQpkZmhlcF9lID0gZGF0YS50YWJsZSgiem9uYXRpb25fcHQiID0gaGVwX3RyYWokZW1ib2xpc2VkKQpkZmhlcF9lID0gZGZoZXBfaFtkZmhlcF9lLCBvbj0iem9uYXRpb25fcHQiLCByb2xsPUluZiwgcm9sbGVuZHMgPSBUXQpkZmhlcF9yID0gZGF0YS50YWJsZSgiem9uYXRpb25fcHQiID0gaGVwX3RyYWokcmVnZW5lcmF0aW5nKQpkZmhlcF9yID0gZGZoZXBfaFtkZmhlcF9yLCBvbj0iem9uYXRpb25fcHQiLCByb2xsPUluZiwgcm9sbGVuZHMgPSBUXQoKaGVwX21lYW5fYmlucyA9IGxpc3QoCmhlYWx0aHkgPSBhcHBseShoZXBfY2VsbHMkaGVhbHRoeUBhc3NheXMkU0NUQGRhdGEsIDEsIAogICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB0YXBwbHkoeCwgZGZoZXBfaCR6b25hdGlvbl9pbnQsIG1lYW4pKSwKZW1ib2xpc2VkID0gYXBwbHkoaGVwX2NlbGxzJGVtYm9saXNlZEBhc3NheXMkU0NUQGRhdGEsIDEsIAogICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB0YXBwbHkoeCwgZGZoZXBfZSR6b25hdGlvbl9pbnQsIG1lYW4pKSwKcmVnZW5lcmF0aW5nID0gYXBwbHkoaGVwX2NlbGxzJHJlZ2VuZXJhdGluZ0Bhc3NheXMkU0NUQGRhdGEsIDEsIAogICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB0YXBwbHkoeCwgZGZoZXBfciR6b25hdGlvbl9pbnQsIG1lYW4pKQopCmZvcihuIGluIG5hbWVzKGhlcF9tZWFuX2JpbnMpKXsgaGVwX21lYW5fYmluc1tbbl1dW2lzLm5hKGhlcF9tZWFuX2JpbnNbW25dXSldID0gMCB9CnNhdmVSRFMoaGVwX21lYW5fYmlucywgZmlsZSA9ICJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvem9uYXRpb25fYmluc19oZXBfbGlzdF9yYW5rLlJEUyIpCgpkZmVuZF9oID0gZGF0YS50YWJsZSgiem9uYXRpb25fcHQiID0gZW5kX3RyYWokaGVhbHRoeSkKZGZlbmRfaCR6b25hdGlvbl9pbnQgPSBjdXQoZGZlbmRfaCR6b25hdGlvbl9wdCwgbmJpbnMpCmRmZW5kX2UgPSBkYXRhLnRhYmxlKCJ6b25hdGlvbl9wdCIgPSBlbmRfdHJhaiRlbWJvbGlzZWQpCmRmZW5kX2UgPSBkZmVuZF9oW2RmZW5kX2UsIG9uPSJ6b25hdGlvbl9wdCIsIHJvbGw9SW5mLCByb2xsZW5kcyA9IFRdCmRmZW5kX3IgPSBkYXRhLnRhYmxlKCJ6b25hdGlvbl9wdCIgPSBlbmRfdHJhaiRyZWdlbmVyYXRpbmcpCmRmZW5kX3IgPSBkZmVuZF9oW2RmZW5kX3IsIG9uPSJ6b25hdGlvbl9wdCIsIHJvbGw9SW5mLCByb2xsZW5kcyA9IFRdCgplbmRfbWVhbl9iaW5zID0gbGlzdCgKaGVhbHRoeSA9IGFwcGx5KGVuZF9jZWxscyRoZWFsdGh5QGFzc2F5cyRTQ1RAZGF0YSwgMSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHRhcHBseSh4LCBkZmVuZF9oJHpvbmF0aW9uX2ludCwgbWVhbikpLAplbWJvbGlzZWQgPSBhcHBseShlbmRfY2VsbHMkZW1ib2xpc2VkQGFzc2F5cyRTQ1RAZGF0YSwgMSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHRhcHBseSh4LCBkZmVuZF9lJHpvbmF0aW9uX2ludCwgbWVhbikpLApyZWdlbmVyYXRpbmcgPSBhcHBseShlbmRfY2VsbHMkcmVnZW5lcmF0aW5nQGFzc2F5cyRTQ1RAZGF0YSwgMSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHRhcHBseSh4LCBkZmVuZF9yJHpvbmF0aW9uX2ludCwgbWVhbikpCikKZm9yKG4gaW4gbmFtZXMoZW5kX21lYW5fYmlucykpeyBlbmRfbWVhbl9iaW5zW1tuXV1baXMubmEoZW5kX21lYW5fYmluc1tbbl1dKV0gPSAwIH0Kc2F2ZVJEUyhlbmRfbWVhbl9iaW5zLCBmaWxlID0gInJlc3VsdHMvem9uYXRpb25fY29uZC96b25hdGlvbl9iaW5zX2VuZF9saXN0X3JhbmsuUkRTIikKYGBgCgpDb3JyZWxhdGUgYmlubmVkIGV4cHJlc3Npb24KCmBgYHtyfQpjb21iX2NvbmQgPSBjb21ibihuYW1lcyhlbmRfY2VsbHMpLCAyKQpoZXBfY29yX2xpc3QgPSBsaXN0KCkKZm9yKGkgaW4gMTpuY29sKGNvbWJfY29uZCkpewogIGMxID0gY29tYl9jb25kWzEsaV0KICBjMiA9IGNvbWJfY29uZFsyLGldCiAgCiAgZ2VuZXMgPSBpbnRlcnNlY3QoY29sbmFtZXMoaGVwX21lYW5fYmluc1tbYzFdXSksY29sbmFtZXMoaGVwX21lYW5fYmluc1tbYzJdXSkpCiAgCiAgZm9yKGcgaW4gZ2VuZXMpewogICAgaGVwX2Nvcl9saXN0W1twYXN0ZTAoYzEsIl8iLGMyKV1dW1tnXV0gPSBjb3IoY2JpbmQoaGVwX21lYW5fYmluc1tbYzFdXVssZ10sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZXBfbWVhbl9iaW5zW1tjMl1dWyxnXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAic3AiKVsxLDJdCiAgfQp9CmhlcF9jb3JfbGlzdCA9IGxhcHBseShoZXBfY29yX2xpc3QsIGRhdGEuZnJhbWUpCmhlcF9jb3JfbGlzdCA9IGxhcHBseShoZXBfY29yX2xpc3QsIGZ1bmN0aW9uKHgpIGNiaW5kKHJvd25hbWVzKHgpLCB4KSkKCmVuZF9jb3JfbGlzdCA9IGxpc3QoKQpmb3IoaSBpbiAxOm5jb2woY29tYl9jb25kKSl7CiAgYzEgPSBjb21iX2NvbmRbMSxpXQogIGMyID0gY29tYl9jb25kWzIsaV0KICAKICBnZW5lcyA9IGludGVyc2VjdChjb2xuYW1lcyhlbmRfbWVhbl9iaW5zW1tjMV1dKSxjb2xuYW1lcyhlbmRfbWVhbl9iaW5zW1tjMl1dKSkKICAKICBmb3IoZyBpbiBnZW5lcyl7CiAgICBlbmRfY29yX2xpc3RbW3Bhc3RlMChjMSwiXyIsYzIpXV1bW2ddXSA9IGNvcihjYmluZChlbmRfbWVhbl9iaW5zW1tjMV1dWyxnXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZF9tZWFuX2JpbnNbW2MyXV1bLGddKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJzcCIpWzEsMl0KICB9Cn0KZW5kX2Nvcl9saXN0ID0gbGFwcGx5KGVuZF9jb3JfbGlzdCwgZGF0YS5mcmFtZSkKZW5kX2Nvcl9saXN0ID0gbGFwcGx5KGVuZF9jb3JfbGlzdCwgZnVuY3Rpb24oeCkgY2JpbmQocm93bmFtZXMoeCksIHgpKQpgYGAKCkNvcnJlbGF0ZSBmaXR0ZWQgZXhwcmVzc2lvbgoKYGBge3J9CmNvbWJfY29uZCA9IGNvbWJuKG5hbWVzKGVuZF9jZWxscyksIDIpCmhlcF9jb3JfZml0X2xpc3QgPSBsaXN0KCkKZm9yKGkgaW4gMTpuY29sKGNvbWJfY29uZCkpewogIGMxID0gY29tYl9jb25kWzEsaV0KICBjMiA9IGNvbWJfY29uZFsyLGldCiAgCiAgZ2VuZXMgPSBpbnRlcnNlY3QoY29sbmFtZXMoaGVwX2ZpdHNfbGlzdFtbYzFdXSksY29sbmFtZXMoaGVwX2ZpdHNfbGlzdFtbYzJdXSkpCiAgCiAgZm9yKGcgaW4gZ2VuZXMpewogICAgaGVwX2Nvcl9maXRfbGlzdFtbcGFzdGUwKGMxLCJfIixjMildXVtbZ11dID0gY29yKGNiaW5kKGhlcF9maXRzX2xpc3RbW2MxXV1bLGddLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVwX2ZpdHNfbGlzdFtbYzJdXVssZ10pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInNwIilbMSwyXQogIH0KfQpoZXBfY29yX2ZpdF9saXN0ID0gbGFwcGx5KGhlcF9jb3JfZml0X2xpc3QsIGRhdGEuZnJhbWUpCmhlcF9jb3JfZml0X2xpc3QgPSBsYXBwbHkoaGVwX2Nvcl9maXRfbGlzdCwgZnVuY3Rpb24oeCkgY2JpbmQocm93bmFtZXMoeCksIHgpKQoKZW5kX2Nvcl9maXRfbGlzdCA9IGxpc3QoKQpmb3IoaSBpbiAxOm5jb2woY29tYl9jb25kKSl7CiAgYzEgPSBjb21iX2NvbmRbMSxpXQogIGMyID0gY29tYl9jb25kWzIsaV0KICAKICBnZW5lcyA9IGludGVyc2VjdChjb2xuYW1lcyhlbmRfZml0c19saXN0W1tjMV1dKSxjb2xuYW1lcyhlbmRfZml0c19saXN0W1tjMl1dKSkKICAKICBmb3IoZyBpbiBnZW5lcyl7CiAgICBlbmRfY29yX2ZpdF9saXN0W1twYXN0ZTAoYzEsIl8iLGMyKV1dW1tnXV0gPSBjb3IoY2JpbmQoZW5kX2ZpdHNfbGlzdFtbYzFdXVssZ10sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmRfZml0c19saXN0W1tjMl1dWyxnXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAic3AiKVsxLDJdCiAgfQp9CmVuZF9jb3JfZml0X2xpc3QgPSBsYXBwbHkoZW5kX2Nvcl9maXRfbGlzdCwgZGF0YS5mcmFtZSkKZW5kX2Nvcl9maXRfbGlzdCA9IGxhcHBseShlbmRfY29yX2ZpdF9saXN0LCBmdW5jdGlvbih4KSBjYmluZChyb3duYW1lcyh4KSwgeCkpCmBgYAoKU2F2ZSBjb3JyZWxhdGlvbnMKCmBgYHtyfQpzYXZlKGhlcF9jb3JfbGlzdCwgZW5kX2Nvcl9saXN0LCAKICAgICBmaWxlID0gInJlc3VsdHMvem9uYXRpb25fY29uZC96b25hdGlvbl9jb3JfY29uZF9saXN0c19yYW5rLlJEYXRhIikKYGBgCgpGaWx0ZXIgcGF0aHdheXMgd2l0aCByaWJvc29tYWwgZ2VuZXMKCmBgYHtyfQpycGZpbHRlciA9IGZ1bmN0aW9uKHgpewogIHJldHVybighZ3JlcGwoIl5SUEwiLCB4JGdlbmVJRCkgJiAhZ3JlcGwoIl5SUFMiLCB4JGdlbmVJRCkgJiAKICAgICAgICAgICAhZ3JlcGwoIi9SUEwiLCB4JGdlbmVJRCwgZml4ZWQgPSBUKSAmICFncmVwbCgiL1JQUyIsIHgkZ2VuZUlELCBmaXhlZCA9IFQpKQp9CmBgYAoKCiMjIEhlcGF0b2N5dGVzCkdldCB0aGUgdG9wIGdlbmVzIGZvciBlYWNoIGNvbmRpdGlvbgoKYGBge3J9CiMgZ2V0IHNpZ25pZmZpY2FudCBnZW5lcyBzaG93aW5nIHZhcmlhdGlvbiAoYXQgbGVhc3QgMC4xIHJhbmdlIGluIHRoZSBmaXR0ZWQgdmFsdWVzKQpnZW5lc19saXN0ID0gbGlzdCgpCmZvcihuIGluIG5hbWVzKGhlcF9maXRzX2xpc3QpKXsKICBoX2ZjX3NwID0gYXBwbHkoaGVwX2ZpdHNfbGlzdFtbbl1dLCAyLCBmdW5jdGlvbih4KSBkaWZmKHJhbmdlKHgpKSkKICBnZW5lc19jID0gbmFtZXMoaGVwX3F2YWxzX2xpc3RbW25dXSlbaGVwX3F2YWxzX2xpc3RbW25dXTw9MC4wNSAmCiAgICAgICAgICAgICAgICAgICAgbmFtZXMoaGVwX3F2YWxzX2xpc3RbW25dXSkgJWluJSBuYW1lcyhoX2ZjX3NwKVtoX2ZjX3NwPj0wLjFdXQogIGdlbmVzX2MgPSBnZW5lc19jW2dlbmVzX2MgJWluJSBuYW1lcyhoZXBfcXZhbHNfbGlzdFtbbl1dKVtvcmRlcihoZXBfcXZhbHNfbGlzdFtbbl1dLCBkZWNyZWFzaW5nID0gRildXQogIGdlbmVzX2xpc3RbW25dXSA9IGdlbmVzX2MKfQpgYGAKCkZpbmQgZW5yaWNoZWQgR08gVGVybXMgYW5kIFJlYWN0b21lIHBhdGh3YXlzCgpgYGB7cn0KY29tYl9jb25kID0gY29tYm4obmFtZXMoaGVwX2ZpdHNfbGlzdCksIDIpCmFsbF9nZW5lc19jb25kcyA9IGxpc3QoKQp0ZXJtc19ncm91cHNfbGlzdCA9IGxpc3QoKQpkZl9jb3JfbGlzdCA9IGxpc3QoKQpmb3IoaSBpbiAxOm5jb2woY29tYl9jb25kKSl7CiAgY29tcCA9IHBhc3RlMChjb21iX2NvbmRbMSxpXSwiXyIsIGNvbWJfY29uZFsyLGldKQogIHByaW50KGNvbWJfY29uZFssaV0pCiAgZ2VuZXNfY29uZHMgPSBsaXN0KCJjb21tb24iID0gaW50ZXJzZWN0KGdlbmVzX2xpc3RbW2NvbWJfY29uZFsxLGldXV0sIGdlbmVzX2xpc3RbW2NvbWJfY29uZFsyLGldXV0pKQogIGdlbmVzX2NvbmRzW1tjb21iX2NvbmRbMSxpXV1dID0gc2V0ZGlmZihnZW5lc19saXN0W1tjb21iX2NvbmRbMSxpXV1dLCBnZW5lc19saXN0W1tjb21iX2NvbmRbMixpXV1dKQogIGdlbmVzX2NvbmRzW1tjb21iX2NvbmRbMixpXV1dID0gc2V0ZGlmZihnZW5lc19saXN0W1tjb21iX2NvbmRbMixpXV1dLCBnZW5lc19saXN0W1tjb21iX2NvbmRbMSxpXV1dKQogIGFsbF9nZW5lc19jb25kc1tbY29tcF1dID0gZ2VuZXNfY29uZHMKICAKICAjIHB1dCB0aGUgZ2VuZXMgaW4gYSBkYXRhIGZyYW1lCiAgZGZfY29yID0gaGVwX2Nvcl9saXN0W1tjb21wXV0KICBkZl9jb3JbLGNvbWJfY29uZFsxLGldXSA9IGRmX2NvciRgcm93bmFtZXMoeClgICVpbiUgZ2VuZXNfY29uZHNbW2NvbWJfY29uZFsxLGldXV0KICBkZl9jb3JbLGNvbWJfY29uZFsyLGldXSAgPSBkZl9jb3IkYHJvd25hbWVzKHgpYCAlaW4lIGdlbmVzX2NvbmRzW1tjb21iX2NvbmRbMixpXV1dCiAgZGZfY29yJGNvbW1vbiA9IGRmX2NvciRgcm93bmFtZXMoeClgICVpbiUgZ2VuZXNfY29uZHMkY29tbW9uCiAgY29sbmFtZXMoZGZfY29yKVsxOjJdID0gYygiZ2VuZSIsICJjb3JyIikKICAKICAjIGxpc3RzIG9mIGdlbmUgZ3JvdXBzIHRvIGNvbXBhcmUgLSBtb3N0IGltcG9ydGFudCBpcyAiYWxsIgogIGdlbmVfZ3JvdXBzX2xpc3QgPSBsaXN0KGNvbW1vbiA9IGFzLmNoYXJhY3RlcihkZl9jb3IkZ2VuZVtkZl9jb3IkY29tbW9uXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgYWxsID0gYXMuY2hhcmFjdGVyKGRmX2NvciRnZW5lWyhkZl9jb3JbW2NvbWJfY29uZFsyLGldXV0gfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9jb3JbW2NvbWJfY29uZFsxLGldXV0gfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9jb3IkY29tbW9uKV0pKQogIGdlbmVfZ3JvdXBzX2xpc3RbW2NvbWJfY29uZFsxLGldXV0gPSBhcy5jaGFyYWN0ZXIoZGZfY29yJGdlbmVbZGZfY29yW1tjb21iX2NvbmRbMSxpXV1dXSkKICBnZW5lX2dyb3Vwc19saXN0W1tjb21iX2NvbmRbMixpXV1dID0gYXMuY2hhcmFjdGVyKGRmX2NvciRnZW5lW2RmX2NvcltbY29tYl9jb25kWzIsaV1dXV0pCiAgCiAgIyBnZXQgZW5yaWNoZWQgZ28gdGVybXMvcGF0aHdheXMKICB0ZXJtc19ncm91cHMgPSBsaXN0KCkKICBmb3IobiBpbiBuYW1lcyhnZW5lX2dyb3Vwc19saXN0KSl7CiAgICAjIGNvbnZlcnQgZ2VuZSBJRHMKICAgIGVnID0gY2x1c3RlclByb2ZpbGVyOjpiaXRyKGdlbmVfZ3JvdXBzX2xpc3RbW25dXSwgZnJvbVR5cGU9IlNZTUJPTCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9UeXBlPSJFTlRSRVpJRCIsIE9yZ0RiPSJvcmcuSHMuZWcuZGIiKQogICAgZWdfYWxsID0gY2x1c3RlclByb2ZpbGVyOjpiaXRyKGFzLmNoYXJhY3RlcihkZl9jb3IkZ2VuZSksIGZyb21UeXBlPSJTWU1CT0wiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b1R5cGU9IkVOVFJFWklEIiwgT3JnRGI9Im9yZy5Icy5lZy5kYiIpCiAgICAKICAgICMgZW5yaWNobWVudAogICAgcmVhYyA9IFJlYWN0b21lUEE6OmVucmljaFBhdGh3YXkoZ2VuZT1lZyRFTlRSRVpJRCxwdmFsdWVDdXRvZmY9MC4wNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFkYWJsZT1ULCB1bml2ZXJzZSA9IGVnX2FsbCRFTlRSRVpJRCkKICAgIGdvdCA9IGNsdXN0ZXJQcm9maWxlcjo6ZW5yaWNoR08oZ2VuZSA9IGVnJEVOVFJFWklELCB1bml2ZXJzZSA9IGVnX2FsbCRFTlRSRVpJRCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9yZ0RiID0gb3JnLkhzLmVnLmRiLCBvbnQgPSAiQlAiLCBwdmFsdWVDdXRvZmYgID0gMC4wMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXZhbHVlQ3V0b2ZmICA9IDAuMDUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwQWRqdXN0TWV0aG9kID0gIkJIIiwgcmVhZGFibGUgPSBUKQogICAgCiAgICBhbGxfdGVybXMgPSByYmluZChhcy5kYXRhLmZyYW1lKHJlYWMpLCBhcy5kYXRhLmZyYW1lKGdvdCkpCiAgICBhbGxfdGVybXMkREIgPSBjKHJlcCgiUmVhY3RvbWUiLCBucm93KGFzLmRhdGEuZnJhbWUocmVhYykpKSwgCiAgICAgICAgICAgICAgICAgICAgIHJlcCgiR08gVGVybSIsIG5yb3coYXMuZGF0YS5mcmFtZShnb3QpKSkpCiAgICBhbGxfdGVybXMgPSBhbGxfdGVybXNbYWxsX3Rlcm1zJHF2YWx1ZTw9MC4wNSxdCiAgICAKICAgIHRlcm1zX2dyb3Vwc1tbbl1dID0gYWxsX3Rlcm1zCiAgfQogIHRlcm1zX2dyb3Vwc19saXN0W1tjb21wXV0gPSB0ZXJtc19ncm91cHMKICBkZl9jb3JfbGlzdFtbY29tcF1dID0gZGZfY29yCn0KYGBgCgpJZGVudGlmeSBwYXRod2F5cyBtb3JlIGFmZmVjdGVkIGJ5IG9uZSBjb25kaXRpb24KCmBgYHtyfQphbm90X2xpc3QgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXModGVybXNfZ3JvdXBzX2xpc3QpKXsKICB0ZXJtc19ncm91cHMgPSB0ZXJtc19ncm91cHNfbGlzdFtbbl1dCiAgZGZfY29yID0gZGZfY29yX2xpc3RbW25dXQogIAogICMgZ2V0IHRoZSBwYXRod2F5cyBmb3IgYWxsIGdlbmVzLCBtaW51cyB0aG9zZSB3aXRoIHJpYm9zb21hbCBnZW5lcwogIGFsbF9wYXRoID0gdGVybXNfZ3JvdXBzJGFsbFtycGZpbHRlcih0ZXJtc19ncm91cHMkYWxsKSxdCiAgYWxsX3BhdGggPSBhbGxfcGF0aFtvcmRlcihhbGxfcGF0aCRxdmFsdWUsIGRlY3JlYXNpbmcgPSBGKSxdCiAgCiAgIyBnZXQgZ2VuZSBncm91cHMgYXMgYSBkYXRhIGZyYW1lCiAgZ19nID0gdW5saXN0KGFwcGx5KGRmX2NvclssYygxLDMsNCw1KV0sIDEsIAogICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSBjb2xuYW1lcyhkZl9jb3JbLGMoMyw0LDUpXSlbd2hpY2goYXMubG9naWNhbCh4WzI6NF0pKV0pKQogIGdlbmVfZ3JvdXAgPSBkYXRhLmZyYW1lKCJnZW5lIiA9IG5hbWVzKGdfZyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgImdyb3VwIiA9IGdfZykKICAKICAjIGdldCBmcmFjdGlvbiBvZiBnZW5lcyBmcm9tIGEgY29uZGl0aW9uIHRoYXQgYXJlIHByZXNlbnQgaW4gYSBnaXZlIHBhdGh3YXkKICBuX2dfZ3JvdXAgPSBsaXN0KCkKICBuX2dfZ3JvdXBbW2xldmVscyhnZW5lX2dyb3VwJGdyb3VwKVsxXV1dID0gYygpCiAgbl9nX2dyb3VwW1tsZXZlbHMoZ2VuZV9ncm91cCRncm91cClbMl1dXSA9IGMoKQogIG5fZ19ncm91cFtbbGV2ZWxzKGdlbmVfZ3JvdXAkZ3JvdXApWzNdXV0gPSBjKCkKICBmb3IoaSBpbiAxOm5yb3coYWxsX3BhdGgpKXsKICAgIGdlbmVzX3AgPSB1bmxpc3Qoc3Ryc3BsaXQoYWxsX3BhdGgkZ2VuZUlEW2ldLCAiLyIpKQogICAgZm9yKGdnIGluIHVuaXF1ZShnZW5lX2dyb3VwJGdyb3VwKSl7CiAgICAgIG5nZyA9IHN1bShnZW5lc19wICVpbiUgZ2VuZV9ncm91cCRnZW5lW2dlbmVfZ3JvdXAkZ3JvdXA9PWdnXSkvc3VtKGdlbmVfZ3JvdXAkZ3JvdXA9PWdnKQogICAgICBuX2dfZ3JvdXBbW2dnXV0gPSBjKG5fZ19ncm91cFtbZ2ddXSwgbmdnKQogICAgfQogIH0KICBhbGxfcGF0aCA9IGNiaW5kKGFsbF9wYXRoLCBkYXRhLmZyYW1lKG5fZ19ncm91cCkpCiAgYWxsX3BhdGgkZGlmZiA9IGFsbF9wYXRoWywxM10gLSBhbGxfcGF0aFssMTJdICMgYWRkIGRpZmZlcmVuY2UgYmV0d2VlbiBjb25kIGFuZCBoZWFsdGh5CiAgYWxsX3BhdGgkaGlnaGVzdCA9IGNvbG5hbWVzKGFsbF9wYXRoKVsxMToxM11bYXBwbHkoYWxsX3BhdGhbLDExOjEzXSwgMSwgd2hpY2gubWF4KV0KICAKICAjIGdyb3VwIHRoZSBkZXRlY3RlZCBwYXRod2F5cyBieSBzaW1pbGFyaXR5CiAgZ29fcGF0aCA9IGFsbF9wYXRoW2FsbF9wYXRoJERCPT0iR08gVGVybSIsXQogIG1hdF9wYXRoID0gbWF0cml4KDAsIG5yb3coZ29fcGF0aCksIG5yb3coZ29fcGF0aCkpCiAgZm9yKGkgaW4gMTpucm93KGdvX3BhdGgpKXsKICAgIGcxID0gZ29fcGF0aFtpLCJnZW5lSUQiXQogICAgZzEgPSB1bmxpc3Qoc3Ryc3BsaXQoZzEsICIvIikpCiAgICBmb3IoaiBpbiAxOm5yb3coZ29fcGF0aCkpewogICAgICAgIGcyID0gZ29fcGF0aFtqLCJnZW5lSUQiXQogICAgICAgIGcyID0gdW5saXN0KHN0cnNwbGl0KGcyLCAiLyIpKQogICAgICAgIG1hdF9wYXRoW2ksal0gPSBsZW5ndGgoaW50ZXJzZWN0KGcxLCBnMikpL21lYW4oYyhsZW5ndGgoZzEpLCBsZW5ndGgoZzIpKSkKICAgIH0KICB9CiAgY2xfZ28gPSBoY2x1c3QoZGlzdChtYXRfcGF0aCksIG1ldGhvZCA9ICJ3YXJkLkQyIikKICBjbF9nbyA9IGN1dHJlZShjbF9nbywgNykKICByb3duYW1lcyhtYXRfcGF0aCkgPSBnb19wYXRoJERlc2NyaXB0aW9uCiAgYW5vdCA9IGRhdGEuZnJhbWUocm93Lm5hbWVzID0gZ29fcGF0aCREZXNjcmlwdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgImNsIiA9IHBhc3RlMCgiY2wiLGNsX2dvKSwgCiAgICAgICAgICAgICAgICAgICAgIm4iID0gZ29fcGF0aCRDb3VudCwKICAgICAgICAgICAgICAgICAgICAiZGlmZiIgPSBnb19wYXRoJGRpZmYsCiAgICAgICAgICAgICAgICAgICAgInF2YWwiID0gLWxvZzEwKGdvX3BhdGgkcXZhbHVlKSwKICAgICAgICAgICAgICAgICAgICAidGVybSIgPSBnb19wYXRoJERlc2NyaXB0aW9uKQogIHBoZWF0bWFwOjpwaGVhdG1hcChtYXRfcGF0aCwgY2x1c3RlcmluZ19tZXRob2QgPSAid2FyZC5EMiIsIAogICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX3JvdyA9IGFub3RbLC01XSwgc2hvd19yb3duYW1lcyA9IEYpCiAgZ2VuZXNfcGVyX2dvY2wgPSB0YXBwbHkoYW5vdCR0ZXJtLCBhbm90JGNsLCAKICAgICAgICAgZnVuY3Rpb24oeCkgdW5pcXVlKHVubGlzdChzdHJzcGxpdChnb19wYXRoW2dvX3BhdGgkRGVzY3JpcHRpb24gJWluJSB4LCJnZW5lSUQiXSwgIi8iKSkpKQogIGdlbmVzX2NvbmRfZ29jbCA9IGxhcHBseShnZW5lc19wZXJfZ29jbCwgZnVuY3Rpb24oeCkgbGFwcGx5KGdlbmVzX2NvbmRzLCBmdW5jdGlvbih5KSBzdW0oeSAlaW4lIHgpKSkKICBnZW5lc19jb25kX2dvY2wgPSBSZWR1Y2UocmJpbmQsIGdlbmVzX2NvbmRfZ29jbCkKICByb3duYW1lcyhnZW5lc19jb25kX2dvY2wpID0gcGFzdGUwKCJjbCIsIDE6NykKICAKICBhbm90X2xpc3RbW25dXSA9IGFub3QKfQpgYGAKClBsb3QgdG9wIHRlcm1zCgpgYGB7cn0KcGRmKCJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvaGVwX3RvcF9nb190ZXJtc19nZW5lcy5wZGYiLCAKICAgIGhlaWdodCA9IDYsIHdpZHRoID0gNywgdXNlRGluZ2JhdHMgPSBGKQpmb3IobiBpbiBuYW1lcyh0ZXJtc19ncm91cHNfbGlzdCkpewogIHRlcm1zX2dyb3VwcyA9IHRlcm1zX2dyb3Vwc19saXN0W1tuXV0KICAKICBmb3IoY29uZCBpbiBuYW1lcyh0ZXJtc19ncm91cHMpKXsKICAgIHBsb3RfZGYgPSB0ZXJtc19ncm91cHNbW2NvbmRdXVtycGZpbHRlcih0ZXJtc19ncm91cHNbW2NvbmRdXSksXQogICAgcGxvdF9kZiA9IHBsb3RfZGZbb3JkZXIocGxvdF9kZiRxdmFsdWUsIGRlY3JlYXNpbmcgPSBGKSxdCiAgICBwbG90X2RmJERlc2NyaXB0aW9uID0gZmFjdG9yKHBsb3RfZGYkRGVzY3JpcHRpb24sIGxldmVscyA9IHJldihwbG90X2RmJERlc2NyaXB0aW9uKSkKICAgIAogICAgcGx0ID0gZ2dwbG90KHBsb3RfZGZbMToxMCxdLCBhZXMoeCA9IERlc2NyaXB0aW9uLCB5ID0gLWxvZzEwKHF2YWx1ZSkpKSsKICAgICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpKwogICAgICBjb29yZF9mbGlwKCkrCiAgICAgIGxhYnModGl0bGUgPSBwYXN0ZTAobiwgIjogIiwgY29uZCkpKwogICAgICB0aGVtZV9idygpCiAgICBwcmludChwbHQpCiAgfQp9CmRldi5vZmYoKQpgYGAKClBsb3Qgc29tZSBnZW5lcwoKYGBge3J9CiMgcGxvdCBhbGwgZ2VuZXMgZnJvbSBwYXRod2F5IGluIGdyZXksIGF2ZXJhZ2UgaW4gY29sb3VyIGZvciBjb25kIGFuZCBoZWFsdGh5CnBsb3RfbGlzdCA9IGxpc3QoKQpleHBfcGx0X2xpc3QgPSBsaXN0KCkKZm9yKGNvbXAgaW4gbmFtZXModGVybXNfZ3JvdXBzX2xpc3QpKXsKICBwcmludChjb21wKQogIGMxID0gc3Ryc3BsaXQoY29tcCwgIl8iKVtbMV1dWzFdCiAgYzIgPSBzdHJzcGxpdChjb21wLCAiXyIpW1sxXV1bMl0KICAKICBwbG90X2xpc3RbW2NvbXBdXSA9IGxpc3QoKQogIGV4cF9wbHRfbGlzdFtbY29tcF1dID0gbGlzdCgpCiAgZm9yKHB3IGluIGMoYzEsIGMyKSl7CiAgICBnb3Rlcm1zID0gdGVybXNfZ3JvdXBzX2xpc3RbW2NvbXBdXVtbcHddXQogICAgZ290ZXJtcyA9IGdvdGVybXNbZ290ZXJtcyREQj09IkdPIFRlcm0iICYgcnBmaWx0ZXIoZ290ZXJtcyksXQogICAgCiAgICBleHBfcGx0X2xpc3RbW2NvbXBdXVtbcHddXSA9IGxpc3QoKQogICAgcGxvdF9saXN0W1tjb21wXV1bW3B3XV0gPSBsaXN0KCkKICAgIGZvcihnbyBpbiAxOm5yb3coZ290ZXJtcykpewogICAgICBnZW5lc19wYXRoID0gdW5saXN0KHN0cnNwbGl0KGdvdGVybXNbZ28sImdlbmVJRCJdLCIvIikpCiAgICAgIAogICAgICBleHBfaGVhbHRoeSA9IHJlc2hhcGUyOjptZWx0KGhlcF9maXRzX2xpc3RbW2MxXV1bLGdlbmVzX3BhdGhdKQogICAgICBleHBfaGVhbHRoeSR4eCA9IHJlcCgxOjEwMCwgbGVuZ3RoKGdlbmVzX3BhdGgpKQogICAgICBleHBfaGVhbHRoeSRjb25kID0gYzEKICAgICAgCiAgICAgIGV4cF9jb25kID0gcmVzaGFwZTI6Om1lbHQoaGVwX2ZpdHNfbGlzdFtbYzJdXVssZ2VuZXNfcGF0aF0pCiAgICAgIGV4cF9jb25kJHh4ID0gcmVwKDE6MTAwLCBsZW5ndGgoZ2VuZXNfcGF0aCkpCiAgICAgIGV4cF9jb25kJGNvbmQgPSBjMgoKICAgICAgZXhwX2RmID0gcmJpbmQoZXhwX2hlYWx0aHksIGV4cF9jb25kKQoKICAgICAgcGx0ID0gZ2dwbG90KGV4cF9kZiwgYWVzKHggPSB4eCwgeSA9IHZhbHVlLCBncm91cCA9IGludGVyYWN0aW9uKHZhcmlhYmxlLCBjb25kKSkpKwogICAgICAgICNnZW9tX2xpbmUobWFwcGluZyA9IGFlcyhjb2xvdXIgPSBjb25kKSwgYWxwaGEgPSAwLjIpKwogICAgICAgIHN0YXRfc21vb3RoKG1hcHBpbmcgPSBhZXMoZ3JvdXAgPSBjb25kLCBjb2xvdXIgPSBjb25kKSwgc2hvdy5sZWdlbmQgPSBUKSsKICAgICAgICBsYWJzKHRpdGxlID0gZ290ZXJtc1tnbywiRGVzY3JpcHRpb24iXSwgCiAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMChjMSwgIiB2cyAiLCBjMiwgIiwgIiwgcHcsICIgcGF0aHdheXMiKSkrCiAgICAgICAgdGhlbWVfY2xhc3NpYygpCiAgICAgIAogICAgICBwbG90X2xpc3RbW2NvbXBdXVtbcHddXVtbZ290ZXJtc1tnbywiRGVzY3JpcHRpb24iXV1dID0gcGx0CiAgICAgIGV4cF9wbHRfbGlzdFtbY29tcF1dW1twd11dW1tnb3Rlcm1zW2dvLCJEZXNjcmlwdGlvbiJdXV0gPSBleHBfZGYKICAgIH0KICB9Cn0KYGBgCgoKYGBge3IsIGZpZy5oZWlnaHQ9Mi43LCBmaWcud2lkdGg9NH0KcGF0aHdheSA9ICJuZWdhdGl2ZSByZWd1bGF0aW9uIG9mIGFwb3B0b3RpYyBzaWduYWxpbmcgcGF0aHdheSIKdW5pcXVlKGV4cF9wbHRfbGlzdCRoZWFsdGh5X3JlZ2VuZXJhdGluZyRyZWdlbmVyYXRpbmdbW3BhdGh3YXldXSR2YXJpYWJsZSkKZ24gPSAiSUNBTTEiCnBsb3RfZGYgPSBleHBfcGx0X2xpc3QkaGVhbHRoeV9yZWdlbmVyYXRpbmckcmVnZW5lcmF0aW5nW1twYXRod2F5XV0KcGxvdF9kZiA9IHBsb3RfZGZbcGxvdF9kZiR2YXJpYWJsZT09Z24sXQpjMSA9ICJoZWFsdGh5IgpjMiA9ICJyZWdlbmVyYXRpbmciCnB3ID0gInJlZ2VuZXJhdGluZyIKZ2dwbG90KHBsb3RfZGYsIGFlcyh4ID0geHgsIHkgPSB2YWx1ZSwgZ3JvdXAgPSBpbnRlcmFjdGlvbih2YXJpYWJsZSwgY29uZCkpKSsKICAgIGdlb21fbGluZShtYXBwaW5nID0gYWVzKGNvbG91ciA9IGNvbmQpKSsKICAgIGxhYnModGl0bGUgPSBwYXRod2F5LCBzdWJ0aXRsZSA9IHBhc3RlMChjMSwgIiB2cyAiLCBjMiwgIiwgIiwgcHcsICIgcGF0aHdheXMiKSwgCiAgICAgICAgIHkgPSBwYXN0ZTAoImxvZyhub3JtIGV4cCkgIiwgZ24pKSsKICAgIHRoZW1lX2NsYXNzaWMoKQpgYGAKClNhdmUgaW1wb3J0YW50IGxpc3RzCgpgYGB7cn0Kc2F2ZSh0ZXJtc19ncm91cHNfbGlzdCwgYW5vdF9saXN0LCBkZl9jb3JfbGlzdCwgZ2VuZXNfbGlzdCwgcGxvdF9saXN0LCBleHBfcGx0X2xpc3QsCiAgICAgZmlsZSA9ICJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvaGVwX2dvX3Jlc3VsdHNfcmFuay5SRGF0YSIpCmBgYAoKCiMjIEVuZG90aGVsaWFsIGNlbGxzCkdldCB0aGUgdG9wIGdlbmVzIGZvciBlYWNoIGNvbmRpdGlvbgoKYGBge3J9CiMgZ2V0IHNpZ25pZmZpY2FudCBnZW5lcyBzaG93aW5nIHZhcmlhdGlvbiAoYXQgbGVhc3QgMC4xIHJhbmdlIGluIHRoZSBmaXR0ZWQgdmFsdWVzKQpnZW5lc19saXN0ID0gbGlzdCgpCmZvcihuIGluIG5hbWVzKGVuZF9maXRzX2xpc3QpKXsKICBoX2ZjX3NwID0gYXBwbHkoZW5kX2ZpdHNfbGlzdFtbbl1dLCAyLCBmdW5jdGlvbih4KSBkaWZmKHJhbmdlKHgpKSkKICBnZW5lc19jID0gbmFtZXMoZW5kX3F2YWxzX2xpc3RbW25dXSlbZW5kX3F2YWxzX2xpc3RbW25dXTw9MC4wNSAmCiAgICAgICAgICAgICAgICAgICAgbmFtZXMoZW5kX3F2YWxzX2xpc3RbW25dXSkgJWluJSBuYW1lcyhoX2ZjX3NwKVtoX2ZjX3NwPj0wLjFdXQogIGdlbmVzX2MgPSBnZW5lc19jW2dlbmVzX2MgJWluJSBuYW1lcyhlbmRfcXZhbHNfbGlzdFtbbl1dKVtvcmRlcihlbmRfcXZhbHNfbGlzdFtbbl1dLCBkZWNyZWFzaW5nID0gRildXQogIGdlbmVzX2xpc3RbW25dXSA9IGdlbmVzX2MKfQpgYGAKCkZpbmQgZW5yaWNoZWQgR08gVGVybXMgYW5kIFJlYWN0b21lIHBhdGh3YXlzCgpgYGB7cn0KY29tYl9jb25kID0gY29tYm4obmFtZXMoZW5kX2ZpdHNfbGlzdCksIDIpCmFsbF9nZW5lc19jb25kcyA9IGxpc3QoKQp0ZXJtc19ncm91cHNfbGlzdCA9IGxpc3QoKQpkZl9jb3JfbGlzdCA9IGxpc3QoKQpmb3IoaSBpbiAxOm5jb2woY29tYl9jb25kKSl7CiAgY29tcCA9IHBhc3RlMChjb21iX2NvbmRbMSxpXSwiXyIsIGNvbWJfY29uZFsyLGldKQogIHByaW50KGNvbWJfY29uZFssaV0pCiAgZ2VuZXNfY29uZHMgPSBsaXN0KCJjb21tb24iID0gaW50ZXJzZWN0KGdlbmVzX2xpc3RbW2NvbWJfY29uZFsxLGldXV0sIGdlbmVzX2xpc3RbW2NvbWJfY29uZFsyLGldXV0pKQogIGdlbmVzX2NvbmRzW1tjb21iX2NvbmRbMSxpXV1dID0gc2V0ZGlmZihnZW5lc19saXN0W1tjb21iX2NvbmRbMSxpXV1dLCBnZW5lc19saXN0W1tjb21iX2NvbmRbMixpXV1dKQogIGdlbmVzX2NvbmRzW1tjb21iX2NvbmRbMixpXV1dID0gc2V0ZGlmZihnZW5lc19saXN0W1tjb21iX2NvbmRbMixpXV1dLCBnZW5lc19saXN0W1tjb21iX2NvbmRbMSxpXV1dKQogIGFsbF9nZW5lc19jb25kc1tbY29tcF1dID0gZ2VuZXNfY29uZHMKICAKICAjIHB1dCB0aGUgZ2VuZXMgaW4gYSBkYXRhIGZyYW1lCiAgZGZfY29yID0gZW5kX2Nvcl9maXRfbGlzdFtbY29tcF1dCiAgZGZfY29yWyxjb21iX2NvbmRbMSxpXV0gPSBkZl9jb3IkYHJvd25hbWVzKHgpYCAlaW4lIGdlbmVzX2NvbmRzW1tjb21iX2NvbmRbMSxpXV1dCiAgZGZfY29yWyxjb21iX2NvbmRbMixpXV0gID0gZGZfY29yJGByb3duYW1lcyh4KWAgJWluJSBnZW5lc19jb25kc1tbY29tYl9jb25kWzIsaV1dXQogIGRmX2NvciRjb21tb24gPSBkZl9jb3IkYHJvd25hbWVzKHgpYCAlaW4lIGdlbmVzX2NvbmRzJGNvbW1vbgogIGNvbG5hbWVzKGRmX2NvcilbMToyXSA9IGMoImdlbmUiLCAiY29yciIpCiAgCiAgIyBsaXN0cyBvZiBnZW5lIGdyb3VwcyB0byBjb21wYXJlIC0gbW9zdCBpbXBvcnRhbnQgaXMgImFsbCIKICBnZW5lX2dyb3Vwc19saXN0ID0gbGlzdChjb21tb24gPSBhcy5jaGFyYWN0ZXIoZGZfY29yJGdlbmVbZGZfY29yJGNvbW1vbl0pLAogICAgICAgICAgICAgICAgICAgICAgICAgIGFsbCA9IGFzLmNoYXJhY3RlcihkZl9jb3IkZ2VuZVsoZGZfY29yW1tjb21iX2NvbmRbMixpXV1dIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGZfY29yW1tjb21iX2NvbmRbMSxpXV1dIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGZfY29yJGNvbW1vbildKSkKICBnZW5lX2dyb3Vwc19saXN0W1tjb21iX2NvbmRbMSxpXV1dID0gYXMuY2hhcmFjdGVyKGRmX2NvciRnZW5lW2RmX2NvcltbY29tYl9jb25kWzEsaV1dXV0pCiAgZ2VuZV9ncm91cHNfbGlzdFtbY29tYl9jb25kWzIsaV1dXSA9IGFzLmNoYXJhY3RlcihkZl9jb3IkZ2VuZVtkZl9jb3JbW2NvbWJfY29uZFsyLGldXV1dKQogIGdlbmVfZ3JvdXBzX2xpc3RbW3Bhc3RlMChjb21iX2NvbmRbMSxpXSwgIl9hbGwiKV1dID0gYyhnZW5lX2dyb3Vwc19saXN0JGNvbW1vbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKGRmX2NvciRnZW5lW2RmX2NvcltbY29tYl9jb25kWzEsaV1dXV0pKQogIGdlbmVfZ3JvdXBzX2xpc3RbW3Bhc3RlMChjb21iX2NvbmRbMixpXSwgIl9hbGwiKV1dID0gYyhnZW5lX2dyb3Vwc19saXN0JGNvbW1vbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKGRmX2NvciRnZW5lW2RmX2NvcltbY29tYl9jb25kWzIsaV1dXV0pKQogIAogICMgZ2V0IGVucmljaGVkIGdvIHRlcm1zL3BhdGh3YXlzCiAgdGVybXNfZ3JvdXBzID0gbGlzdCgpCiAgZWdfYWxsID0gY2x1c3RlclByb2ZpbGVyOjpiaXRyKGFzLmNoYXJhY3RlcihkZl9jb3IkZ2VuZSksIGZyb21UeXBlPSJTWU1CT0wiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b1R5cGU9IkVOVFJFWklEIiwgT3JnRGI9Im9yZy5Icy5lZy5kYiIpCiAgZm9yKG4gaW4gbmFtZXMoZ2VuZV9ncm91cHNfbGlzdCkpewogICAgIyBjb252ZXJ0IGdlbmUgSURzCiAgICBlZyA9IGNsdXN0ZXJQcm9maWxlcjo6Yml0cihnZW5lX2dyb3Vwc19saXN0W1tuXV0sIGZyb21UeXBlPSJTWU1CT0wiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvVHlwZT0iRU5UUkVaSUQiLCBPcmdEYj0ib3JnLkhzLmVnLmRiIikKICAgIAogICAgIyBlbnJpY2htZW50CiAgICByZWFjID0gUmVhY3RvbWVQQTo6ZW5yaWNoUGF0aHdheShnZW5lID0gZWckRU5UUkVaSUQsIHB2YWx1ZUN1dG9mZj0wLjA1LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYWRhYmxlPVQsIHVuaXZlcnNlID0gZWdfYWxsJEVOVFJFWklEKQogICAgZ290ID0gY2x1c3RlclByb2ZpbGVyOjplbnJpY2hHTyhnZW5lID0gZWckRU5UUkVaSUQsIHVuaXZlcnNlID0gZWdfYWxsJEVOVFJFWklELCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT3JnRGIgPSBvcmcuSHMuZWcuZGIsIG9udCA9ICJCUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiAgPSAwLjAxLCBxdmFsdWVDdXRvZmYgID0gMC4wNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLCByZWFkYWJsZSA9IFQpCiAgICAKICAgIGFsbF90ZXJtcyA9IHJiaW5kKGFzLmRhdGEuZnJhbWUocmVhYyksIGFzLmRhdGEuZnJhbWUoZ290KSkKICAgIGFsbF90ZXJtcyREQiA9IGMocmVwKCJSZWFjdG9tZSIsIG5yb3coYXMuZGF0YS5mcmFtZShyZWFjKSkpLCAKICAgICAgICAgICAgICAgICAgICAgcmVwKCJHTyBUZXJtIiwgbnJvdyhhcy5kYXRhLmZyYW1lKGdvdCkpKSkKICAgIGFsbF90ZXJtcyA9IGFsbF90ZXJtc1thbGxfdGVybXMkcXZhbHVlPD0wLjA1LF0KICAgIAogICAgdGVybXNfZ3JvdXBzW1tuXV0gPSBhbGxfdGVybXMKICB9CiAgdGVybXNfZ3JvdXBzX2xpc3RbW2NvbXBdXSA9IHRlcm1zX2dyb3VwcwogIGRmX2Nvcl9saXN0W1tjb21wXV0gPSBkZl9jb3IKfQpgYGAKCklkZW50aWZ5IHBhdGh3YXlzIG1vcmUgYWZmZWN0ZWQgYnkgb25lIGNvbmRpdGlvbgoKYGBge3J9CmFub3RfbGlzdCA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyh0ZXJtc19ncm91cHNfbGlzdCkpewogIHRlcm1zX2dyb3VwcyA9IHRlcm1zX2dyb3Vwc19saXN0W1tuXV0KICBkZl9jb3IgPSBkZl9jb3JfbGlzdFtbbl1dCiAgCiAgIyBnZXQgdGhlIHBhdGh3YXlzIGZvciBhbGwgZ2VuZXMsIG1pbnVzIHRob3NlIHdpdGggcmlib3NvbWFsIGdlbmVzCiAgYWxsX3BhdGggPSB0ZXJtc19ncm91cHMkYWxsW3JwZmlsdGVyKHRlcm1zX2dyb3VwcyRhbGwpLF0KICBhbGxfcGF0aCA9IGFsbF9wYXRoW29yZGVyKGFsbF9wYXRoJHF2YWx1ZSwgZGVjcmVhc2luZyA9IEYpLF0KICAKICAjIGdldCBnZW5lIGdyb3VwcyBhcyBhIGRhdGEgZnJhbWUKICBnX2cgPSB1bmxpc3QoYXBwbHkoZGZfY29yWyxjKDEsMyw0LDUpXSwgMSwgCiAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIGNvbG5hbWVzKGRmX2NvclssYygzLDQsNSldKVt3aGljaChhcy5sb2dpY2FsKHhbMjo0XSkpXSkpCiAgZ2VuZV9ncm91cCA9IGRhdGEuZnJhbWUoImdlbmUiID0gbmFtZXMoZ19nKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAiZ3JvdXAiID0gZ19nKQogIAogICMgZ2V0IGZyYWN0aW9uIG9mIGdlbmVzIGZyb20gYSBjb25kaXRpb24gdGhhdCBhcmUgcHJlc2VudCBpbiBhIGdpdmUgcGF0aHdheQogIG5fZ19ncm91cCA9IGxpc3QoKQogIG5fZ19ncm91cFtbbGV2ZWxzKGdlbmVfZ3JvdXAkZ3JvdXApWzFdXV0gPSBjKCkKICBuX2dfZ3JvdXBbW2xldmVscyhnZW5lX2dyb3VwJGdyb3VwKVsyXV1dID0gYygpCiAgbl9nX2dyb3VwW1tsZXZlbHMoZ2VuZV9ncm91cCRncm91cClbM11dXSA9IGMoKQogIGZvcihpIGluIDE6bnJvdyhhbGxfcGF0aCkpewogICAgZ2VuZXNfcCA9IHVubGlzdChzdHJzcGxpdChhbGxfcGF0aCRnZW5lSURbaV0sICIvIikpCiAgICBmb3IoZ2cgaW4gdW5pcXVlKGdlbmVfZ3JvdXAkZ3JvdXApKXsKICAgICAgbmdnID0gc3VtKGdlbmVzX3AgJWluJSBnZW5lX2dyb3VwJGdlbmVbZ2VuZV9ncm91cCRncm91cD09Z2ddKS9zdW0oZ2VuZV9ncm91cCRncm91cD09Z2cpCiAgICAgIG5fZ19ncm91cFtbZ2ddXSA9IGMobl9nX2dyb3VwW1tnZ11dLCBuZ2cpCiAgICB9CiAgfQogIGFsbF9wYXRoID0gY2JpbmQoYWxsX3BhdGgsIGRhdGEuZnJhbWUobl9nX2dyb3VwKSkKICBhbGxfcGF0aCRkaWZmID0gYWxsX3BhdGhbLDEzXSAtIGFsbF9wYXRoWywxMl0gIyBhZGQgZGlmZmVyZW5jZSBiZXR3ZWVuIGNvbmQgYW5kIGhlYWx0aHkKICBhbGxfcGF0aCRoaWdoZXN0ID0gY29sbmFtZXMoYWxsX3BhdGgpWzExOjEzXVthcHBseShhbGxfcGF0aFssMTE6MTNdLCAxLCB3aGljaC5tYXgpXQogIAogICMgZ3JvdXAgdGhlIGRldGVjdGVkIHBhdGh3YXlzIGJ5IHNpbWlsYXJpdHkKICBnb19wYXRoID0gYWxsX3BhdGhbYWxsX3BhdGgkREI9PSJHTyBUZXJtIixdCiAgbWF0X3BhdGggPSBtYXRyaXgoMCwgbnJvdyhnb19wYXRoKSwgbnJvdyhnb19wYXRoKSkKICBmb3IoaSBpbiAxOm5yb3coZ29fcGF0aCkpewogICAgZzEgPSBnb19wYXRoW2ksImdlbmVJRCJdCiAgICBnMSA9IHVubGlzdChzdHJzcGxpdChnMSwgIi8iKSkKICAgIGZvcihqIGluIDE6bnJvdyhnb19wYXRoKSl7CiAgICAgICAgZzIgPSBnb19wYXRoW2osImdlbmVJRCJdCiAgICAgICAgZzIgPSB1bmxpc3Qoc3Ryc3BsaXQoZzIsICIvIikpCiAgICAgICAgbWF0X3BhdGhbaSxqXSA9IGxlbmd0aChpbnRlcnNlY3QoZzEsIGcyKSkvbWVhbihjKGxlbmd0aChnMSksIGxlbmd0aChnMikpKQogICAgfQogIH0KICBjbF9nbyA9IGhjbHVzdChkaXN0KG1hdF9wYXRoKSwgbWV0aG9kID0gIndhcmQuRDIiKQogIGNsX2dvID0gY3V0cmVlKGNsX2dvLCA3KQogIHJvd25hbWVzKG1hdF9wYXRoKSA9IGdvX3BhdGgkRGVzY3JpcHRpb24KICBhbm90ID0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSBnb19wYXRoJERlc2NyaXB0aW9uLCAKICAgICAgICAgICAgICAgICAgICAiY2wiID0gcGFzdGUwKCJjbCIsY2xfZ28pLCAKICAgICAgICAgICAgICAgICAgICAibiIgPSBnb19wYXRoJENvdW50LAogICAgICAgICAgICAgICAgICAgICJkaWZmIiA9IGdvX3BhdGgkZGlmZiwKICAgICAgICAgICAgICAgICAgICAicXZhbCIgPSAtbG9nMTAoZ29fcGF0aCRxdmFsdWUpLAogICAgICAgICAgICAgICAgICAgICJ0ZXJtIiA9IGdvX3BhdGgkRGVzY3JpcHRpb24pCiAgcGhlYXRtYXA6OnBoZWF0bWFwKG1hdF9wYXRoLCBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQyIiwgCiAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fcm93ID0gYW5vdFssLTVdLCBzaG93X3Jvd25hbWVzID0gRikKICBnZW5lc19wZXJfZ29jbCA9IHRhcHBseShhbm90JHRlcm0sIGFub3QkY2wsIAogICAgICAgICBmdW5jdGlvbih4KSB1bmlxdWUodW5saXN0KHN0cnNwbGl0KGdvX3BhdGhbZ29fcGF0aCREZXNjcmlwdGlvbiAlaW4lIHgsImdlbmVJRCJdLCAiLyIpKSkpCiAgZ2VuZXNfY29uZF9nb2NsID0gbGFwcGx5KGdlbmVzX3Blcl9nb2NsLCBmdW5jdGlvbih4KSBsYXBwbHkoZ2VuZXNfY29uZHMsIGZ1bmN0aW9uKHkpIHN1bSh5ICVpbiUgeCkpKQogIGdlbmVzX2NvbmRfZ29jbCA9IFJlZHVjZShyYmluZCwgZ2VuZXNfY29uZF9nb2NsKQogIHJvd25hbWVzKGdlbmVzX2NvbmRfZ29jbCkgPSBwYXN0ZTAoImNsIiwgMTo3KQogIAogIGFub3RfbGlzdFtbbl1dID0gYW5vdAp9CmBgYAoKUGxvdCB0b3AgdGVybXMKCmBgYHtyfQpwZGYoInJlc3VsdHMvem9uYXRpb25fY29uZC9lbmRfdG9wX2dvX3Rlcm1zX2dlbmVzLnBkZiIsIAogICAgaGVpZ2h0ID0gNiwgd2lkdGggPSA3LCB1c2VEaW5nYmF0cyA9IEYpCmZvcihuIGluIG5hbWVzKHRlcm1zX2dyb3Vwc19saXN0KSl7CiAgdGVybXNfZ3JvdXBzID0gdGVybXNfZ3JvdXBzX2xpc3RbW25dXQogIAogIGZvcihjb25kIGluIG5hbWVzKHRlcm1zX2dyb3VwcykpewogICAgcGxvdF9kZiA9IHRlcm1zX2dyb3Vwc1tbY29uZF1dW3JwZmlsdGVyKHRlcm1zX2dyb3Vwc1tbY29uZF1dKSxdCiAgICBwbG90X2RmID0gcGxvdF9kZltvcmRlcihwbG90X2RmJHF2YWx1ZSwgZGVjcmVhc2luZyA9IEYpLF0KICAgIHBsb3RfZGYkRGVzY3JpcHRpb24gPSBmYWN0b3IocGxvdF9kZiREZXNjcmlwdGlvbiwgbGV2ZWxzID0gcmV2KHBsb3RfZGYkRGVzY3JpcHRpb24pKQogICAgCiAgICBwbHQgPSBnZ3Bsb3QocGxvdF9kZlsxOjEwLF0sIGFlcyh4ID0gRGVzY3JpcHRpb24sIHkgPSAtbG9nMTAocXZhbHVlKSkpKwogICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikrCiAgICAgIGNvb3JkX2ZsaXAoKSsKICAgICAgbGFicyh0aXRsZSA9IHBhc3RlMChuLCAiOiAiLCBjb25kKSkrCiAgICAgIHRoZW1lX2J3KCkKICAgIHByaW50KHBsdCkKICB9Cn0KZGV2Lm9mZigpCmBgYAoKUGxvdCBzb21lIGdlbmVzCgpgYGB7cn0KIyBwbG90IGFsbCBnZW5lcyBmcm9tIHBhdGh3YXkgaW4gZ3JleSwgYXZlcmFnZSBpbiBjb2xvdXIgZm9yIGNvbmQgYW5kIGhlYWx0aHkKcGxvdF9saXN0ID0gbGlzdCgpCmV4cF9wbHRfbGlzdCA9IGxpc3QoKQpmb3IoY29tcCBpbiBuYW1lcyh0ZXJtc19ncm91cHNfbGlzdCkpewogIHByaW50KGNvbXApCiAgYzEgPSBzdHJzcGxpdChjb21wLCAiXyIpW1sxXV1bMV0KICBjMiA9IHN0cnNwbGl0KGNvbXAsICJfIilbWzFdXVsyXQogIAogIHBsb3RfbGlzdFtbY29tcF1dID0gbGlzdCgpCiAgZXhwX3BsdF9saXN0W1tjb21wXV0gPSBsaXN0KCkKICBmb3IocHcgaW4gYyhjMSwgYzIpKXsKICAgIGdvdGVybXMgPSB0ZXJtc19ncm91cHNfbGlzdFtbY29tcF1dW1twd11dCiAgICBnb3Rlcm1zID0gZ290ZXJtc1tnb3Rlcm1zJERCPT0iR08gVGVybSIgJiBycGZpbHRlcihnb3Rlcm1zKSxdCiAgICAKICAgIGV4cF9wbHRfbGlzdFtbY29tcF1dW1twd11dID0gbGlzdCgpCiAgICBwbG90X2xpc3RbW2NvbXBdXVtbcHddXSA9IGxpc3QoKQogICAgaWYobnJvdyhnb3Rlcm1zKT4wKXsKICAgICAgZm9yKGdvIGluIDE6bnJvdyhnb3Rlcm1zKSl7CiAgICAgICAgZ2VuZXNfcGF0aCA9IHVubGlzdChzdHJzcGxpdChnb3Rlcm1zW2dvLCJnZW5lSUQiXSwiLyIpKQogICAgICAgIAogICAgICAgIGV4cF9oZWFsdGh5ID0gcmVzaGFwZTI6Om1lbHQoZW5kX2ZpdHNfbGlzdFtbYzFdXVssZ2VuZXNfcGF0aF0pCiAgICAgICAgZXhwX2hlYWx0aHkkeHggPSByZXAoMToxMDAsIGxlbmd0aChnZW5lc19wYXRoKSkKICAgICAgICBleHBfaGVhbHRoeSRjb25kID0gYzEKICAgICAgICAKICAgICAgICBleHBfY29uZCA9IHJlc2hhcGUyOjptZWx0KGVuZF9maXRzX2xpc3RbW2MyXV1bLGdlbmVzX3BhdGhdKQogICAgICAgIGV4cF9jb25kJHh4ID0gcmVwKDE6MTAwLCBsZW5ndGgoZ2VuZXNfcGF0aCkpCiAgICAgICAgZXhwX2NvbmQkY29uZCA9IGMyCiAgCiAgICAgICAgZXhwX2RmID0gcmJpbmQoZXhwX2hlYWx0aHksIGV4cF9jb25kKQogIAogICAgICAgIHBsdCA9IGdncGxvdChleHBfZGYsIGFlcyh4ID0geHgsIHkgPSB2YWx1ZSwgZ3JvdXAgPSBpbnRlcmFjdGlvbih2YXJpYWJsZSwgY29uZCkpKSsKICAgICAgICAgICNnZW9tX2xpbmUobWFwcGluZyA9IGFlcyhjb2xvdXIgPSBjb25kKSwgYWxwaGEgPSAwLjIpKwogICAgICAgICAgc3RhdF9zbW9vdGgobWFwcGluZyA9IGFlcyhncm91cCA9IGNvbmQsIGNvbG91ciA9IGNvbmQpLCBzaG93LmxlZ2VuZCA9IFQpKwogICAgICAgICAgbGFicyh0aXRsZSA9IGdvdGVybXNbZ28sIkRlc2NyaXB0aW9uIl0sIAogICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMChjMSwgIiB2cyAiLCBjMiwgIiwgIiwgcHcsICIgcGF0aHdheXMiKSkrCiAgICAgICAgICB0aGVtZV9jbGFzc2ljKCkKICAgICAgICAKICAgICAgICBwbG90X2xpc3RbW2NvbXBdXVtbcHddXVtbZ290ZXJtc1tnbywiRGVzY3JpcHRpb24iXV1dID0gcGx0CiAgICAgICAgZXhwX3BsdF9saXN0W1tjb21wXV1bW3B3XV1bW2dvdGVybXNbZ28sIkRlc2NyaXB0aW9uIl1dXSA9IGV4cF9kZgogICAgICB9CiAgICB9CiAgfQp9CmBgYAoKCmBgYHtyLCBmaWcuaGVpZ2h0PTIuNywgZmlnLndpZHRoPTR9CnBhdGh3YXkgPSAiY2VsbHVsYXIgcmVzcG9uc2UgdG8gdHlwZSBJIGludGVyZmVyb24iCnVuaXF1ZShleHBfcGx0X2xpc3QkaGVhbHRoeV9yZWdlbmVyYXRpbmckcmVnZW5lcmF0aW5nW1twYXRod2F5XV0kdmFyaWFibGUpCmduID0gIkVHUjEiCnBsb3RfZGYgPSBleHBfcGx0X2xpc3QkaGVhbHRoeV9yZWdlbmVyYXRpbmckcmVnZW5lcmF0aW5nW1twYXRod2F5XV0KcGxvdF9kZiA9IHBsb3RfZGZbcGxvdF9kZiR2YXJpYWJsZT09Z24sXQpjMSA9ICJoZWFsdGh5IgpjMiA9ICJyZWdlbmVyYXRpbmciCnB3ID0gInJlZ2VuZXJhdGluZyIKZ2dwbG90KHBsb3RfZGYsIGFlcyh4ID0geHgsIHkgPSB2YWx1ZSwgZ3JvdXAgPSBpbnRlcmFjdGlvbih2YXJpYWJsZSwgY29uZCkpKSsKICAgIGdlb21fbGluZShtYXBwaW5nID0gYWVzKGNvbG91ciA9IGNvbmQpKSsKICAgIGxhYnModGl0bGUgPSBwYXRod2F5LCBzdWJ0aXRsZSA9IHBhc3RlMChjMSwgIiB2cyAiLCBjMiwgIiwgIiwgcHcsICIgcGF0aHdheXMiKSwgCiAgICAgICAgIHkgPSBwYXN0ZTAoImxvZyhub3JtIGV4cCkgIiwgZ24pKSsKICAgIHRoZW1lX2NsYXNzaWMoKQpgYGAKClNhdmUgaW1wb3J0YW50IGxpc3RzCgpgYGB7cn0Kc2F2ZSh0ZXJtc19ncm91cHNfbGlzdCwgYW5vdF9saXN0LCBkZl9jb3JfbGlzdCwgcGxvdF9saXN0LCBleHBfcGx0X2xpc3QsCiAgICAgZmlsZSA9ICJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvZW5kX2dvX3Jlc3VsdHNfcmFuay5SRGF0YSIpCmBgYAoKCgoKCgoKCgoKCgoKIyBPTEQgQ09ERQpGaW5kIGdlbmVzIHdpdGggZGlmZmVyZW5jaWFsIHZhcmlhdGlvbiAtIG5vdCB2ZXJ5IGludGVyZXN0aW5nIHNpbmNlIG1vc3QgZ2VuZXMgc2VlbSB0byBiZSBzaG93aW5nIGEgdmFyaWF0aW9uCgpgYGB7cn0KY2VsbHMgPSB1bmxpc3QobGFwcGx5KGhlcF9jZWxscywgY29sbmFtZXMpKQpnZW5lcyA9IHVuaXF1ZSh1bmxpc3QobGFwcGx5KGhlcF9jZWxscywgVmFyaWFibGVGZWF0dXJlcykpKQpleHBfaGVwID0gYWxsY2VsbHNfY3NzW2dlbmVzLGNlbGxzXUBhc3NheXMkU0NUQGRhdGEKZ2VuZXMgPSByb3duYW1lcyhleHBfaGVwKQoKZGZfbWV0YSA9IGRhdGEuZnJhbWUoInB0IiA9IGMoZGZfZGMkREMxLCBlbWJfcHJlZCwgcmVnX3ByZWQpLAogICAgICAgICAgICAgICAgICAgICAiY29uZCIgPSBhbGxjZWxsc19jc3NbZ2VuZXMsY2VsbHNdQG1ldGEuZGF0YSRDb25kaXRpb24pCgojIEZpdCBHQU0gZm9yIGVhY2ggZ2VuZSB1c2luZyBwc2V1ZG90aW1lIGFzIGluZGVwZW5kZW50IHZhcmlhYmxlLgp0ID0gZGZfbWV0YSRwdApjb25kID0gZGZfbWV0YSRjb25kCmdlbmVfZml0X2ludF9wID0gbWF0cml4KG5yb3cgPSAwLCBuY29sID0gMykKY29sbmFtZXMoZ2VuZV9maXRfaW50X3ApID0gYygic3BsaW5lIiwgImNvbmRpdGlvbiIsICJpbnRlcmFjdGlvbiIpCmdlbmVfZml0X2ludF92YWxzID0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSAxOjMwMCkKcHJlZGdyaWQgPSBkYXRhLmZyYW1lKCJ0IiA9IHJlcChzZXEobWluKHQpLCBtYXgodCksIGxlbmd0aC5vdXQgPSAxMDApLCAzKSwKICAgICAgICAgICAgICAgICAgICAgICJjb25kIiA9IHJlcChuYW1lcyhoZXBfY2VsbHMpLCBlYWNoID0gMTAwKSkKZm9yKGkgaW4gZ2VuZXMpewogIHogPSBleHBfaGVwW2ksXQogIAogIGQgPSBkYXRhLmZyYW1lKHo9eiwgdD10LCBjb25kPWNvbmQpCiAgdG1wID0gc3VwcHJlc3NNZXNzYWdlcyhnYW0oeiB+IG5zKHQsIGRmID0gNCkqY29uZCwgZGF0YT1kKSkKICAKICAjIGJpbnMgZm9yIG1vZGVsIGZpdHRpbmcKICBnZW5lX2ZpdF9pbnRfdmFsc1ssZ10gPSBzdXBwcmVzc01lc3NhZ2VzKHByZWRpY3QodG1wLCBuZXdkYXRhID0gcHJlZGdyaWQpKQogIHAgPSBzdW1tYXJ5KHRtcCkkcGFyYW1ldHJpYy5hbm92YSRgUHIoPkYpYFsxOjNdCiAgZ2VuZV9maXRfaW50X3AgPSByYmluZChnZW5lX2ZpdF9pbnRfcCwgcCkKICByb3duYW1lcyhnZW5lX2ZpdF9pbnRfcClbbnJvdyhnZW5lX2ZpdF9pbnRfcCldID0gaQp9CmBgYAoKRFRXIGNvbmRpdGlvbiB2cyBjb25kaXRpb24KCmBgYHtyfQpjb25kX2NvbWIgPSBjb21ibihuYW1lcyhxdmFsc19saXN0KSwgMikKZm9yKGkgaW4gMTpuY29sKGNvbmRfY29tYikpewogIGdlbmVzID0gaW50ZXJzZWN0KG5hbWVzKHF2YWxzX2xpc3RbW2NvbmRfY29tYlsxLGldXV0pW3F2YWxzX2xpc3RbW2NvbmRfY29tYlsxLGldXV08PTAuMDVdLAogICAgICAgICAgICAgICAgICAgIG5hbWVzKHF2YWxzX2xpc3RbW2NvbmRfY29tYlsyLGldXV0pW3F2YWxzX2xpc3RbW2NvbmRfY29tYlsyLGldXV08PTAuMDVdKQogIAogIHNjX2xpc3QgPSBsaXN0KCkKICBmb3IoY29uZCBpbiBjb25kX2NvbWJbLGldKXsKICAgIGdlbmVfZml0X3ZhbHMgPSBmaXRzX2xpc3RbW2NvbmRdXQogICAgZ2VuZV9maXRfdmFsc19maWx0ID0gdChnZW5lX2ZpdF92YWxzWyxnZW5lc10pCiAgICBzY19saXN0W1tjb25kXV0gPSB0KGFwcGx5KGdlbmVfZml0X3ZhbHNfZmlsdCwgMSwgc2NhbGUpKQogIH0KICAKICB4eHggPSBkdHcoc2NfbGlzdFtbMV1dLCBzY19saXN0W1syXV0sIHdpbmRvdy50eXBlID0gIml0YWt1cmEiLCBkaXN0Lm1ldGhvZCA9ICJEVFciKQogIHl5eSA9IGR0dyhkaXN0KHQoc2NfbGlzdFtbMV1dKSwgdChzY19saXN0W1syXV0pLCBtZXRob2QgPSAiRFRXIiksIHdpbmRvdy50eXBlID0gIml0YWt1cmEiKQp9CmBgYAoKCgpgYGB7cn0Kc291cmNlKCJhdXhfc2NyaXB0cy9wcmltYXRlX2NlcmVicmFsX29yZ2Fub2lkc19wdF9hbGlnbm1lbnQuciIpCgp4Y3YgPSBhbGlnbl9wdF90cmFqKGV4cHJfcmVmID0gaGVwX2NlbGxzJGhlYWx0aHlAYXNzYXlzJFNDVEBkYXRhW2dlbmVzLF0sIAogICAgICAgICAgICAgICAgICAgIHB0X3JlZiA9IHRyYWpfbGlzdCRoZWFsdGh5LAogICAgICAgICAgICAgICAgICAgIGV4cHJfcXVlcnkgPSBoZXBfY2VsbHMkZW1ib2xpc2VkQGFzc2F5cyRTQ1RAZGF0YVtnZW5lcyxdLCAKICAgICAgICAgICAgICAgICAgICBwdF9xdWVyeSA9IHRyYWpfbGlzdCRlbWJvbGlzZWQsIG51bV9icmVha3NfcmVmID0gMTAwLCAKICAgICAgICAgICAgICAgICAgICByZWZfbmFtZSA9ICJoZWFsdGh5IiwgcXVlcnlfbmFtZSA9ICJlbWJvbGlzZWQiKQoKcGxvdCh0cmFqX2xpc3QkZW1ib2xpc2VkLCB4Y3YkcHRfcXVlcnlfYWxpZ25lZCkKYWJsaW5lKDAsMSkKCnhjYiA9IGFsaWduX3B0X3RyYWooZXhwcl9yZWYgPSBoZXBfY2VsbHMkaGVhbHRoeUBhc3NheXMkU0NUQGRhdGFbZ2VuZXMsXSwgCiAgICAgICAgICAgICAgICAgICAgcHRfcmVmID0gdHJhal9saXN0JGhlYWx0aHksCiAgICAgICAgICAgICAgICAgICAgZXhwcl9xdWVyeSA9IGhlcF9jZWxscyRyZWdlbmVyYXRpbmdAYXNzYXlzJFNDVEBkYXRhW2dlbmVzLF0sIAogICAgICAgICAgICAgICAgICAgIHB0X3F1ZXJ5ID0gdHJhal9saXN0JHJlZ2VuZXJhdGluZywgbnVtX2JyZWFrc19yZWYgPSAxMDAsIAogICAgICAgICAgICAgICAgICAgIHJlZl9uYW1lID0gImhlYWx0aHkiLCBxdWVyeV9uYW1lID0gInJlZ2VuZXJhdGluZyIpCgpwbG90KHRyYWpfbGlzdCRyZWdlbmVyYXRpbmcsIHhjYiRwdF9xdWVyeV9hbGlnbmVkKQphYmxpbmUoMCwxKQpgYGAKCmBgYHtyfQpnID0gIkNMRUMxQiIKcGx0X2RmID0gZGF0YS5mcmFtZSgidHJhaiIgPSBjKHRyYWpfbGlzdCRyZWdlbmVyYXRpbmcsIHRyYWpfbGlzdCRoZWFsdGh5KSwKICAgICAgICAgICAiZXhwIiA9IGMoZW5kX2NlbGxzJHJlZ2VuZXJhdGluZ0Bhc3NheXMkU0NUQGRhdGFbZyxuYW1lcyh0cmFqX2xpc3QkcmVnZW5lcmF0aW5nKV0sCiAgICAgICAgICAgZW5kX2NlbGxzJGhlYWx0aHlAYXNzYXlzJFNDVEBkYXRhW2csXSksCiAgICAgICAgICAgImNvbmQiID0gYyhyZXAoInJlZ2VuZXJhdGluZyIsIGxlbmd0aCh0cmFqX2xpc3QkcmVnZW5lcmF0aW5nKSksCiAgICAgICAgICAgICAgICAgICAgICByZXAoImhlYWx0aHkiLCBsZW5ndGgodHJhal9saXN0JGhlYWx0aHkpKSksCiAgICAgICAgICAgImNsIiA9IGMoZW5kX2NlbGxzJHJlZ2VuZXJhdGluZ0BtZXRhLmRhdGEkYWxsY2VsbHNfc2ltcCwKICAgICAgICAgICAgICAgICAgICBlbmRfY2VsbHMkaGVhbHRoeUBtZXRhLmRhdGEkYWxsY2VsbHNfc2ltcCkpCnBsdF9kZiA9IHBsdF9kZltvcmRlcihwbHRfZGYkdHJhaiwgZGVjcmVhc2luZyA9IFQpLF0KCmdncGxvdChwbHRfZGYsIGFlcyh4ID0gdHJhaiwgeSA9IGV4cCwgZ3JvdXAgPSBjb25kLCBjb2xvdXIgPSBjb25kKSkrCiAgZ2VvbV9wb2ludCgpKwogIHN0YXRfc21vb3RoKCkrCiAgbGFicyh0aXRsZSA9IGcpKwogIHRoZW1lX2J3KCkKCmdncGxvdChwbHRfZGYsIGFlcyh4ID0gdHJhaiwgeSA9IGV4cCwgZ3JvdXAgPSBjb25kLCBjb2xvdXIgPSBjbCwgc2hhcGUgPSBjb25kKSkrCiAgZ2VvbV9wb2ludCgpKwogIHN0YXRfc21vb3RoKCkrCiAgbGFicyh0aXRsZSA9IGcpKwogIHRoZW1lX2J3KCkKCnh4eCA9IGR0dzo6ZHR3KHggPSBwbHRfZGZbcGx0X2RmJGNvbmQ9PSJoZWFsdGh5IiwiZXhwIl0sIHkgPSBwbHRfZGZbcGx0X2RmJGNvbmQ9PSJyZWdlbmVyYXRpbmciLCJleHAiXSkKcGxvdCh4eHgpCmFibGluZSgwLDEpCgpodGkgPSBjdXRfbnVtYmVyKHBsdF9kZiR0cmFqW3BsdF9kZiRjb25kPT0iaGVhbHRoeSJdLCAxMDApCnJ0aSA9IGN1dF9udW1iZXIocGx0X2RmJHRyYWpbcGx0X2RmJGNvbmQ9PSJyZWdlbmVyYXRpbmciXSwgMTAwKQpkZjIgPSBkYXRhLmZyYW1lKCJyIiA9IHRhcHBseShwbHRfZGYkZXhwW3BsdF9kZiRjb25kPT0icmVnZW5lcmF0aW5nIl0sIHJ0aSwgbWVhbiksCiAgICAgICAgICAgICAgICAgImgiID0gdGFwcGx5KHBsdF9kZiRleHBbcGx0X2RmJGNvbmQ9PSJoZWFsdGh5Il0sIGh0aSwgbWVhbikpCnl5eSA9IGR0dzo6ZHR3KHggPSBkZjIkaCwgeSA9IGRmMiRyKQpwbG90KHl5eSkKYWJsaW5lKDAsMSkKYGBgCgoKCgpHZXQgR08gVGVybSBlbnJpY2htZW50CgpgYGB7cn0KbGlicmFyeSh0b3BHTykKY29yX2xpc3RzID0gbGlzdCgiaGVwIiA9IGhlcF9jb3JfbGlzdCwgImVuZCIgPSBlbmRfY29yX2xpc3QpCgpjYyA9IC0wLjI1CmdvX2Vucl9saXN0ID0gbGlzdCgpCmZvcihnciBpbiBuYW1lcyhjb3JfbGlzdHMpKXsKICBjb3JfbGlzdCA9IGNvcl9saXN0c1tbZ3JdXQoKICBnb19lbnJfbGlzdFtbZ3JdXSA9IGxpc3QoKQogIGZvcihjb21wIGluIG5hbWVzKGNvcl9saXN0KSl7CiAgICB0ZXN0ZGYgPSBjb3JfbGlzdFtbY29tcF1dCiAgICAKICAgIAogICAgCiAgICBuY29uZCA9IHN0cnNwbGl0KGNvbXAsICJfIilbWzFdXQogICAgZ29fZW5yX2xpc3RbW2dyXV1bW2NvbXBdXSA9IGxpc3QoKQogICAgZm9yKGNvbmQgaW4gbmNvbmQpewogICAgICBnc2lnID0gbmFtZXMoaGVwX3F2YWxbW2NvbmRdXSlbaGVwX3F2YWxbW2NvbmRdXTw9MC4wNV0KICAgICAgCiAgICAgIHN1YnRlc3RkZiA9IG1lcmdlKHRlc3RkZiwgaGVwX3F2YWxbW2NvbmRdXSwgYnkgPSAwKQogICAgICAKICAgICAgZ2VuZXMgPSBzdWJ0ZXN0ZGYkWC4uaS4uCiAgICAgIG5hbWVzKGdlbmVzKSA9IHN1YnRlc3RkZlssMV0KICAgICAgCiAgICAgICMgR08gZW5yaWNobWVudAogICAgICBmc2VsID0gZnVuY3Rpb24oeCkgcmV0dXJuKHg8Y2MgJiBzdWJ0ZXN0ZGYkeTw9MC4wNSkKICAgICAgCiAgICAgIHNhbXBsZUdPZGF0YSA8LSBuZXcoInRvcEdPZGF0YSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb24gPSAidGVzdCIsIG9udG9sb2d5ID0gIkJQIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBhbGxHZW5lcyA9IGdlbmVzLCBnZW5lU2VsID0gZnNlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICBub2RlU2l6ZSA9IDUsIGFubm90ID0gYW5uRlVOLm9yZywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWFwcGluZyA9ICJvcmcuSHMuZWcuZGIiLCBJRCA9ICJzeW1ib2wiKQogICAgICAKICAgICAgcmVzdWx0S1MuZWxpbSA8LSBydW5UZXN0KHNhbXBsZUdPZGF0YSwgYWxnb3JpdGhtID0gImVsaW0iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRpc3RpYyA9ICJmaXNoZXIiKQogICAgICAKICAgICAgcmVzR08gPSBHZW5UYWJsZShvYmplY3QgPSBzYW1wbGVHT2RhdGEsIGVsaW1LUyA9IHJlc3VsdEtTLmVsaW0sCiAgICAgICAgICAgICAgICAgICAgICAgdG9wTm9kZXMgPSByZXN1bHRLUy5lbGltQGdlbmVEYXRhWzRdLCBudW1DaGFyID0gMTAwMCkKICAgICAgcmVzR08kZWxpbUtTID0gc2NvcmUocmVzdWx0S1MuZWxpbSlbcmVzR08kR08uSURdCiAgICAgIHJlc0dPJGNvbmQgPSBjb25kCiAgICAgIAogICAgICAjIGdldCBnZW5lcwogICAgICByZWxldmFudF9nZW5lcyA9IHNhbXBsZUdPZGF0YUBhbGxHZW5lc1tzYW1wbGVHT2RhdGFAZ2VuZVNlbGVjdGlvbkZ1bihzYW1wbGVHT2RhdGFAYWxsU2NvcmVzKV0KICAgICAgZ29HZW5lcyA9IHQoZGF0YS5mcmFtZShsYXBwbHkoZ2VuZXNJblRlcm0oc2FtcGxlR09kYXRhKSwgZnVuY3Rpb24oeCkgcGFzdGUoeFt4ICVpbiUgcmVsZXZhbnRfZ2VuZXNdLCBjb2xsYXBzZSA9ICIsICIpKSkpCiAgICAgIHJvd25hbWVzKGdvR2VuZXMpID0gZ3N1YigiLiIsICI6Iiwgcm93bmFtZXMoZ29HZW5lcyksIGZpeGVkID0gVCkKICAgICAgY29sbmFtZXMoZ29HZW5lcykgPSAiZ2VuZXMiCiAgICAgIHJlc0dPID0gbWVyZ2UocmVzR08sIGdvR2VuZXMsIGJ5LnggPSAxLCBieS55ID0gMCwgYWxsLnggPSBUKQogICAgICAKICAgICAgaWYoY29uZD09bmNvbmRbWzFdXSl7CiAgICAgICAgZ29fZW5yX2xpc3RbW2dyXV1bW2NvbXBdXSA9IHJlc0dPWyxjKCJHTy5JRCIsIlRlcm0iLCJBbm5vdGF0ZWQiLCJTaWduaWZpY2FudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFeHBlY3RlZCIsImVsaW1LUyIsImNvbmQiLCJnZW5lcyIpXQogICAgICB9IGVsc2V7CiAgICAgICAgZ29fZW5yX2xpc3RbW2dyXV1bW2NvbXBdXSA9IHJiaW5kKGdvX2Vucl9saXN0W1tncl1dW1tjb21wXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc0dPWyxjKCJHTy5JRCIsIlRlcm0iLCJBbm5vdGF0ZWQiLCJTaWduaWZpY2FudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFeHBlY3RlZCIsImVsaW1LUyIsImNvbmQiLCJnZW5lcyIpXSkKICAgICAgfQogICAgICAKICAgIH0KICB9Cn0Kc2F2ZVJEUyhnb19lbnJfbGlzdCwgZmlsZSA9ICJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvZ29fZW5yX2NvcnNpZ19saXN0LlJEUyIpCmBgYAoKCmBgYHtyfQpnZyA9IHVuaXF1ZShjKG5hbWVzKGVuZF9xdmFsJGhlYWx0aHkpW29yZGVyKGVuZF9xdmFsJGhlYWx0aHksIGRlY3JlYXNpbmcgPSBGKV1bMToxMDAwXSwKICAgICAgICAgICAgICBuYW1lcyhlbmRfcXZhbCRyZWdlbmVyYXRpbmcpW29yZGVyKGVuZF9xdmFsJHJlZ2VuZXJhdGluZywgZGVjcmVhc2luZyA9IEYpXVsxOjEwMDBdKSkKZGlmZl9maXRzID0gYXBwbHkoZW5kX2ZpdHMkaGVhbHRoeVssZ2ddLCAyLCByYW5rKS1hcHBseShlbmRfZml0cyRyZWdlbmVyYXRpbmdbLGdnXSwgMiwgcmFuaykKY2wgPSBoY2x1c3QoZGlzdCh0KGRpZmZfZml0cykpLCBtZXRob2QgPSAid2FyZC5EIikKY3QgPSBjdXRyZWUoY2wsIDYpCmRmID0gZGF0YS5mcmFtZSgiY29yIiA9IGVuZF9jb3JfZml0X2xpc3QkaGVhbHRoeV9yZWdlbmVyYXRpbmdbZ2csMl0sCiAgICAgICAgICAgICAgICAgInNjb3JlIiA9IGFwcGx5KGFwcGx5KHQoZGlmZl9maXRzKSwgMSwgcmFuZ2UpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMiwgZnVuY3Rpb24oeCkgc3VtKGFicyh4KSkpKQpkaWZmX2dlbmVzID0gcm93bmFtZXMoZGYpW2RmJGNvcjwwLjI1XQpjbCA9IGhjbHVzdChkaXN0KHQoZGlmZl9maXRzKVtkaWZmX2dlbmVzLF0pLCBtZXRob2QgPSAid2FyZC5EIikKY3QgPSBjdXRyZWUoY2wsIDQpCmRmID0gZGF0YS5mcmFtZSgiY2wiID0gZmFjdG9yKGN0KSwKICAgICAgICAgICAgICAgICJjb3IiID0gMS1lbmRfY29yX2ZpdF9saXN0JGhlYWx0aHlfcmVnZW5lcmF0aW5nW2RpZmZfZ2VuZXMsMl0sCiAgICAgICAgICAgICAgICAgInNjb3JlIiA9IGFwcGx5KGFwcGx5KHQoZGlmZl9maXRzKVtkaWZmX2dlbmVzLF0sIDEsIHJhbmdlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDIsIGZ1bmN0aW9uKHgpIHN1bShhYnMoeCkpKSkKcGhlYXRtYXA6OnBoZWF0bWFwKHQoZGlmZl9maXRzKVtkaWZmX2dlbmVzLF0sIGNsdXN0ZXJfY29scyA9IEYsIAogICAgICAgICAgICAgICAgICAgY2x1c3RlcmluZ19tZXRob2QgPSAid2FyZC5EIiwgZm9udHNpemVfcm93ID0gOCwgYW5ub3RhdGlvbl9yb3cgPSBkZikKCnBsb3QoaGVwX2ZpdHMkaGVhbHRoeVssIlNBQTIiXSkKcGxvdChoZXBfZml0cyRyZWdlbmVyYXRpbmdbLCJTQUEyIl0pCmBgYAoKYGBge3J9CmZzZWwgPSBmdW5jdGlvbih4KSByZXR1cm4oeDwwLjI1ICYgbmFtZXMoZ2VuZXMpICVpbiUgZ2cpCmZzZWwgPSBmdW5jdGlvbih4KSByZXR1cm4obmFtZXMoZ2VuZXMpICVpbiUgZ2cpCmdlbmVzID0gZW5kX2Nvcl9maXRfbGlzdCRoZWFsdGh5X3JlZ2VuZXJhdGluZyRYLi5pLi4KbmFtZXMoZ2VuZXMpID0gZW5kX2Nvcl9maXRfbGlzdCRoZWFsdGh5X3JlZ2VuZXJhdGluZ1ssMV0KZ2VuZXMgPSBnZW5lc1shaXMubmEoZ2VuZXMpXQoKZWcgPSBjbHVzdGVyUHJvZmlsZXI6OmJpdHIobmFtZXMoZ2VuZXNbZnNlbChnZW5lcyldKSwgZnJvbVR5cGU9IlNZTUJPTCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICB0b1R5cGU9IkVOVFJFWklEIiwgT3JnRGI9Im9yZy5Icy5lZy5kYiIpCmVnX2FsbCA9IGNsdXN0ZXJQcm9maWxlcjo6Yml0cihuYW1lcyhnZW5lcyksIGZyb21UeXBlPSJTWU1CT0wiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9UeXBlPSJFTlRSRVpJRCIsIE9yZ0RiPSJvcmcuSHMuZWcuZGIiKQoKeCA8LSBSZWFjdG9tZVBBOjplbnJpY2hQYXRod2F5KGdlbmU9ZWckRU5UUkVaSUQscHZhbHVlQ3V0b2ZmPTAuMDUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhZGFibGU9VCwgdW5pdmVyc2UgPSBlZ19hbGwkRU5UUkVaSUQpCmNsdXN0ZXJQcm9maWxlcjo6ZG90cGxvdCh4KQpoZWFkKGFzLmRhdGEuZnJhbWUoeCkpCgplZ28zIDwtIGNsdXN0ZXJQcm9maWxlcjo6ZW5yaWNoR08oZ2VuZSA9IGVnJEVOVFJFWklELCB1bml2ZXJzZSA9IGVnX2FsbCRFTlRSRVpJRCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiA9IG9yZy5Icy5lZy5kYiwgb250ID0gIkJQIiwgcHZhbHVlQ3V0b2ZmICA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdmFsdWVDdXRvZmYgID0gMC4wNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwQWRqdXN0TWV0aG9kID0gIkJIIiwgcmVhZGFibGUgPSBUKQpjbHVzdGVyUHJvZmlsZXI6OmRvdHBsb3QoZWdvMykKYGBgCgoKCmBgYHtyfQpnZyA9IHVuaXF1ZShjKG5hbWVzKHF2YWxzX2JfbGlzdCRoZWFsdGh5KVtvcmRlcihxdmFsc19iX2xpc3QkaGVhbHRoeSwgZGVjcmVhc2luZyA9IEYpXVsxOjEwMDBdLAogICAgICAgICAgICAgIG5hbWVzKHF2YWxzX2JfbGlzdCRyZWdlbmVyYXRpbmcpW29yZGVyKHF2YWxzX2JfbGlzdCRyZWdlbmVyYXRpbmcsIGRlY3JlYXNpbmcgPSBGKV1bMToxMDAwXSkpCmRpZmZfZml0cyA9IGFwcGx5KGZpdHNfYl9saXN0JGhlYWx0aHlbLGdnXSwgMiwgcmFuayktYXBwbHkoZml0c19iX2xpc3QkcmVnZW5lcmF0aW5nWyxnZ10sIDIsIHJhbmspCmNsID0gaGNsdXN0KGRpc3QodChkaWZmX2ZpdHMpKSwgbWV0aG9kID0gIndhcmQuRCIpCmN0ID0gY3V0cmVlKGNsLCA2KQpkZiA9IGRhdGEuZnJhbWUoImNvciIgPSBoZXBfY29yX2ZpdF9saXN0JGhlYWx0aHlfcmVnZW5lcmF0aW5nW2dnLDJdLAogICAgICAgICAgICAgICAgICJzY29yZSIgPSBhcHBseShhcHBseSh0KGRpZmZfZml0cyksIDEsIHJhbmdlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDIsIGZ1bmN0aW9uKHgpIHN1bShhYnMoeCkpKSkKZGYgPSBkZltjb21wbGV0ZS5jYXNlcyhkZiksXQpkaWZmX2dlbmVzID0gcm93bmFtZXMoZGYpW2RmJGNvcjwwLjI1XQpjbCA9IGhjbHVzdChkaXN0KHQoZGlmZl9maXRzKVtkaWZmX2dlbmVzLF0pLCBtZXRob2QgPSAid2FyZC5EIikKY3QgPSBjdXRyZWUoY2wsIDQpCmRmID0gZGF0YS5mcmFtZSgiY2wiID0gZmFjdG9yKGN0KSwKICAgICAgICAgICAgICAgICJjb3IiID0gMS1oZXBfY29yX2ZpdF9saXN0JGhlYWx0aHlfcmVnZW5lcmF0aW5nW2RpZmZfZ2VuZXMsMl0sCiAgICAgICAgICAgICAgICAgInNjb3JlIiA9IGFwcGx5KGFwcGx5KHQoZGlmZl9maXRzKVtkaWZmX2dlbmVzLF0sIDEsIHJhbmdlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDIsIGZ1bmN0aW9uKHgpIHN1bShhYnMoeCkpKSkKcGhlYXRtYXA6OnBoZWF0bWFwKHQoZGlmZl9maXRzKVtkaWZmX2dlbmVzLF0sIGNsdXN0ZXJfY29scyA9IEYsIAogICAgICAgICAgICAgICAgICAgY2x1c3RlcmluZ19tZXRob2QgPSAid2FyZC5EIiwgZm9udHNpemVfcm93ID0gOCwgYW5ub3RhdGlvbl9yb3cgPSBkZikKCnBsb3QoaGVwX2ZpdHMkaGVhbHRoeVssIkNQUzEiXSkKcGxvdChoZXBfZml0cyRyZWdlbmVyYXRpbmdbLCJDUFMxIl0pCmBgYAoKCmBgYHtyfQpnZW5lc19oZWFsdGh5ID0gbmFtZXMocXZhbHNfYl9saXN0JGhlYWx0aHkpW29yZGVyKHF2YWxzX2JfbGlzdCRoZWFsdGh5LCBkZWNyZWFzaW5nID0gRildW3F2YWxzX2JfbGlzdCRoZWFsdGh5W29yZGVyKHF2YWxzX2JfbGlzdCRoZWFsdGh5LCBkZWNyZWFzaW5nID0gRildPD0wLjA1XQpnZW5lc19yZWdlbiA9IG5hbWVzKHF2YWxzX2JfbGlzdCRyZWdlbmVyYXRpbmcpW29yZGVyKHF2YWxzX2JfbGlzdCRyZWdlbmVyYXRpbmcsIGRlY3JlYXNpbmcgPSBGKV1bcXZhbHNfYl9saXN0JHJlZ2VuZXJhdGluZ1tvcmRlcihxdmFsc19iX2xpc3QkcmVnZW5lcmF0aW5nLCBkZWNyZWFzaW5nID0gRildPD0wLjA1XQpnZyA9IHVuaXF1ZShjKGdlbmVzX2hlYWx0aHksIGdlbmVzX3JlZ2VuKSkKCmVnID0gY2x1c3RlclByb2ZpbGVyOjpiaXRyKGdnLCBmcm9tVHlwZT0iU1lNQk9MIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvVHlwZT0iRU5UUkVaSUQiLCBPcmdEYj0ib3JnLkhzLmVnLmRiIikKZWdfYWxsID0gY2x1c3RlclByb2ZpbGVyOjpiaXRyKG5hbWVzKHF2YWxzX2JfbGlzdCRoZWFsdGh5KSwgZnJvbVR5cGU9IlNZTUJPTCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICB0b1R5cGU9IkVOVFJFWklEIiwgT3JnRGI9Im9yZy5Icy5lZy5kYiIpCgpyZWFjID0gUmVhY3RvbWVQQTo6ZW5yaWNoUGF0aHdheShnZW5lPWVnJEVOVFJFWklELHB2YWx1ZUN1dG9mZj0wLjA1LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYWRhYmxlPVQsIHVuaXZlcnNlID0gZWdfYWxsJEVOVFJFWklEKQoKZ290ID0gY2x1c3RlclByb2ZpbGVyOjplbnJpY2hHTyhnZW5lID0gZWckRU5UUkVaSUQsIHVuaXZlcnNlID0gZWdfYWxsJEVOVFJFWklELCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiA9IG9yZy5Icy5lZy5kYiwgb250ID0gIkJQIiwgcHZhbHVlQ3V0b2ZmICA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXZhbHVlQ3V0b2ZmICA9IDAuMDUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLCByZWFkYWJsZSA9IFQpCgphbGxfdGVybXMgPSByYmluZChhcy5kYXRhLmZyYW1lKHJlYWMpLCBhcy5kYXRhLmZyYW1lKGdvdCkpCmFsbF90ZXJtcyREQiA9IGMocmVwKCJSZWFjdG9tZSIsIG5yb3coYXMuZGF0YS5mcmFtZShyZWFjKSkpLCByZXAoIkdPIFRlcm0iLCBucm93KGFzLmRhdGEuZnJhbWUoZ290KSkpKQphbGxfdGVybXMgPSBhbGxfdGVybXNbYWxsX3Rlcm1zJHF2YWx1ZTw9MC4wNSxdCmFsbF90ZXJtc19ub3JpYm8gPSBhbGxfdGVybXNbIWdyZXBsKCJSUEwiLCBhbGxfdGVybXMkZ2VuZUlEKSAmICFncmVwbCgiUlBTIiwgYWxsX3Rlcm1zJGdlbmVJRCksXQoKbmhlYWx0aHkgPSBjKCkKbnJlZ2VuID0gYygpCmNvcmhlYWx0aHkgPSBjKCkKY29ycmVnZW4gPSBjKCkKbWVhbl90cmFqX2ggPSBtYXRyaXgoMCwgbnJvdyhhbGxfdGVybXNfbm9yaWJvKSwgMTAwKQptZWFuX3RyYWpfciA9IG1hdHJpeCgwLCBucm93KGFsbF90ZXJtc19ub3JpYm8pLCAxMDApCmNvcl9tZWFuID0gYygpCmZvcihpIGluIDE6bnJvdyhhbGxfdGVybXNfbm9yaWJvKSl7CiAgZ2VuZXNfcGF0aCA9IHVubGlzdChzdHJzcGxpdChhbGxfdGVybXNfbm9yaWJvW2ksImdlbmVJRCJdLCIvIikpCiAgaW5faGVhbHRoeSA9IGdlbmVzX3BhdGhbZ2VuZXNfcGF0aCAlaW4lIGdlbmVzX2hlYWx0aHldCiAgaW5fcmVnZW4gPSBnZW5lc19wYXRoW2dlbmVzX3BhdGggJWluJSBnZW5lc19yZWdlbl0KICAKICBuaGVhbHRoeSA9IGMobmhlYWx0aHksIGxlbmd0aChpbl9oZWFsdGh5KSkKICBucmVnZW4gPSBjKG5yZWdlbiwgbGVuZ3RoKGluX3JlZ2VuKSkKICAKICBpZihsZW5ndGgoaW5faGVhbHRoeSk+MSl7CiAgICBjaCA9IGNvcihmaXRzX2JfbGlzdCRoZWFsdGh5Wyxpbl9oZWFsdGh5XSwgbWV0aG9kID0gInNwIikKICAgIGNvcmhlYWx0aHkgPSBjKGNvcmhlYWx0aHksIG1lZGlhbihjaFt1cHBlci50cmkoY2gpXSkpIAogIH0gZWxzZXsgCiAgICBjb3JoZWFsdGh5ID0gYyhjb3JoZWFsdGh5LDEpCiAgfQogIGlmKGxlbmd0aChpbl9yZWdlbik+MSl7CiAgICBjaCA9IGNvcihmaXRzX2JfbGlzdCRyZWdlblssaW5fcmVnZW5dLCBtZXRob2QgPSAic3AiKQogICAgY29ycmVnZW4gPSBjKGNvcnJlZ2VuLCBtZWRpYW4oY2hbdXBwZXIudHJpKGNoKV0pKSAKICB9IGVsc2V7IAogICAgY29ycmVnZW4gPSBjKGNvcnJlZ2VuLDEpCiAgfQogIAogIG1lYW5fdHJhal9oW2ksXSA9IGlmKGxlbmd0aChpbl9oZWFsdGh5KT4xKXsgcm93TWVhbnMoZml0c19iX2xpc3QkaGVhbHRoeVssaW5faGVhbHRoeV0pfSBlbHNlIGlmKGxlbmd0aChpbl9oZWFsdGh5KT4wKXtmaXRzX2JfbGlzdCRoZWFsdGh5Wyxpbl9oZWFsdGh5XX0gZWxzZSB7cmVwKDAsMTAwKX0KICBtZWFuX3RyYWpfcltpLF0gPSBpZihsZW5ndGgoaW5fcmVnZW4pPjEpeyByb3dNZWFucyhmaXRzX2JfbGlzdCRyZWdlbmVyYXRpbmdbLGluX3JlZ2VuXSl9IGVsc2UgaWYobGVuZ3RoKGluX3JlZ2VuKT4wKXtmaXRzX2JfbGlzdCRyZWdlbmVyYXRpbmdbLGluX3JlZ2VuXX0gZWxzZSB7cmVwKDAsMTAwKX0KICAKICBjb3JfbWVhbiA9IGMoY29yX21lYW4sIGNvcihtZWFuX3RyYWpfaFtpLF0sIG1lYW5fdHJhal9yW2ksXSkpCn0KYWxsX3Rlcm1zX25vcmlibyA9IGNiaW5kKGFsbF90ZXJtc19ub3JpYm8sIG5oZWFsdGh5LCBucmVnZW4sIGNvcmhlYWx0aHksIGNvcnJlZ2VuLCBjb3JfbWVhbikKcm93bmFtZXMobWVhbl90cmFqX2gpID0gcm93bmFtZXMobWVhbl90cmFqX3IpID0gYWxsX3Rlcm1zX25vcmlibyREZXNjcmlwdGlvbgoKClZpZXcoYWxsX3Rlcm1zX25vcmlib1thbGxfdGVybXNfbm9yaWJvJGNvcmhlYWx0aHk+PTAuMjUgJiBhbGxfdGVybXNfbm9yaWJvJGNvcnJlZ2VuPj0wLjI1ICYKICAgICAgICAgICAgICAgICAgICAgICAgYWxsX3Rlcm1zX25vcmlibyRuaGVhbHRoeT49MiAmIGFsbF90ZXJtc19ub3JpYm8kbnJlZ2VuPj0yLF0pCmkgPSB3aGljaChhbGxfdGVybXNfbm9yaWJvJERlc2NyaXB0aW9uPT0ibVJOQSBTcGxpY2luZyAtIE1pbm9yIFBhdGh3YXkiKQpgYGAKCgoKYGBge3J9CmdlbmVzX2hlYWx0aHkgPSBuYW1lcyhxdmFsc19iX2xpc3QkaGVhbHRoeSlbcXZhbHNfYl9saXN0JGhlYWx0aHk8PTAuMDVdCmdlbmVzX3JlZ2VuID0gbmFtZXMocXZhbHNfYl9saXN0JHJlZ2VuZXJhdGluZylbcXZhbHNfYl9saXN0JHJlZ2VuZXJhdGluZzw9MC4wNV0KZ2VuZXNfY29uZHMgPSBsaXN0KCJoZWFsdGh5IiA9IHNldGRpZmYoZ2VuZXNfaGVhbHRoeSwgZ2VuZXNfcmVnZW4pLCAKICAgICAgICAgICAgICAgICAgICJyZWdlbiIgPSBzZXRkaWZmKGdlbmVzX3JlZ2VuLCBnZW5lc19oZWFsdGh5KSwKICAgICAgICAgICAgICAgICAgICJjb21tb24iID0gaW50ZXJzZWN0KGdlbmVzX2hlYWx0aHksIGdlbmVzX3JlZ2VuKSkKCnRlcm1zX2xpc3QgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXMoZ2VuZXNfY29uZHMpKXsKICBlZyA9IGNsdXN0ZXJQcm9maWxlcjo6Yml0cihnZW5lc19jb25kc1tbbl1dLCBmcm9tVHlwZT0iU1lNQk9MIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvVHlwZT0iRU5UUkVaSUQiLCBPcmdEYj0ib3JnLkhzLmVnLmRiIikKICBlZ19hbGwgPSBjbHVzdGVyUHJvZmlsZXI6OmJpdHIobmFtZXMocXZhbHNfYl9saXN0JGhlYWx0aHkpLCBmcm9tVHlwZT0iU1lNQk9MIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9UeXBlPSJFTlRSRVpJRCIsIE9yZ0RiPSJvcmcuSHMuZWcuZGIiKQogIAogIHJlYWMgPSBSZWFjdG9tZVBBOjplbnJpY2hQYXRod2F5KGdlbmU9ZWckRU5UUkVaSUQscHZhbHVlQ3V0b2ZmPTAuMDUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFkYWJsZT1ULCB1bml2ZXJzZSA9IGVnX2FsbCRFTlRSRVpJRCkKICAKICBnb3QgPSBjbHVzdGVyUHJvZmlsZXI6OmVucmljaEdPKGdlbmUgPSBlZyRFTlRSRVpJRCwgdW5pdmVyc2UgPSBlZ19hbGwkRU5UUkVaSUQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT3JnRGIgPSBvcmcuSHMuZWcuZGIsIG9udCA9ICJCUCIsIHB2YWx1ZUN1dG9mZiAgPSAwLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXZhbHVlQ3V0b2ZmICA9IDAuMDUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcEFkanVzdE1ldGhvZCA9ICJCSCIsIHJlYWRhYmxlID0gVCkKICAKICBhbGxfdGVybXMgPSByYmluZChhcy5kYXRhLmZyYW1lKHJlYWMpLCBhcy5kYXRhLmZyYW1lKGdvdCkpCiAgYWxsX3Rlcm1zJERCID0gYyhyZXAoIlJlYWN0b21lIiwgbnJvdyhhcy5kYXRhLmZyYW1lKHJlYWMpKSksIHJlcCgiR08gVGVybSIsIG5yb3coYXMuZGF0YS5mcmFtZShnb3QpKSkpCiAgYWxsX3Rlcm1zID0gYWxsX3Rlcm1zW2FsbF90ZXJtcyRxdmFsdWU8PTAuMDUsXQogIGFsbF90ZXJtc19ub3JpYm8gPSBhbGxfdGVybXNbIWdyZXBsKCJSUEwiLCBhbGxfdGVybXMkZ2VuZUlEKSAmICFncmVwbCgiUlBTIiwgYWxsX3Rlcm1zJGdlbmVJRCksXQogIAogIG5oZWFsdGh5ID0gYygpCiAgbnJlZ2VuID0gYygpCiAgY29yaGVhbHRoeSA9IGMoKQogIGNvcnJlZ2VuID0gYygpCiAgbWVhbl90cmFqX2ggPSBtYXRyaXgoMCwgbnJvdyhhbGxfdGVybXNfbm9yaWJvKSwgMTAwKQogIG1lYW5fdHJhal9yID0gbWF0cml4KDAsIG5yb3coYWxsX3Rlcm1zX25vcmlibyksIDEwMCkKICBjb3JfbWVhbiA9IGMoKQogIGZvcihpIGluIDE6bnJvdyhhbGxfdGVybXNfbm9yaWJvKSl7CiAgICBnZW5lc19wYXRoID0gdW5saXN0KHN0cnNwbGl0KGFsbF90ZXJtc19ub3JpYm9baSwiZ2VuZUlEIl0sIi8iKSkKICAgIGluX2hlYWx0aHkgPSBnZW5lc19wYXRoW2dlbmVzX3BhdGggJWluJSBnZW5lc19oZWFsdGh5XQogICAgaW5fcmVnZW4gPSBnZW5lc19wYXRoW2dlbmVzX3BhdGggJWluJSBnZW5lc19yZWdlbl0KICAgIAogICAgbmhlYWx0aHkgPSBjKG5oZWFsdGh5LCBsZW5ndGgoaW5faGVhbHRoeSkpCiAgICBucmVnZW4gPSBjKG5yZWdlbiwgbGVuZ3RoKGluX3JlZ2VuKSkKICAgIAogICAgaWYobGVuZ3RoKGluX2hlYWx0aHkpPjEpewogICAgICBjaCA9IGNvcihmaXRzX2JfbGlzdCRoZWFsdGh5Wyxpbl9oZWFsdGh5XSwgbWV0aG9kID0gInNwIikKICAgICAgY29yaGVhbHRoeSA9IGMoY29yaGVhbHRoeSwgbWVkaWFuKGNoW3VwcGVyLnRyaShjaCldKSkgCiAgICB9IGVsc2V7IAogICAgICBjb3JoZWFsdGh5ID0gYyhjb3JoZWFsdGh5LDApCiAgICB9CiAgICBpZihsZW5ndGgoaW5fcmVnZW4pPjEpewogICAgICBjaCA9IGNvcihmaXRzX2JfbGlzdCRyZWdlblssaW5fcmVnZW5dLCBtZXRob2QgPSAic3AiKQogICAgICBjb3JyZWdlbiA9IGMoY29ycmVnZW4sIG1lZGlhbihjaFt1cHBlci50cmkoY2gpXSkpIAogICAgfSBlbHNleyAKICAgICAgY29ycmVnZW4gPSBjKGNvcnJlZ2VuLDApCiAgICB9CiAgICAKICAgIG1lYW5fdHJhal9oW2ksXSA9IGlmKGxlbmd0aChpbl9oZWFsdGh5KT4xKXsgcm93TWVhbnMoZml0c19iX2xpc3QkaGVhbHRoeVssaW5faGVhbHRoeV0pfSBlbHNlIGlmKGxlbmd0aChpbl9oZWFsdGh5KT4wKXtmaXRzX2JfbGlzdCRoZWFsdGh5Wyxpbl9oZWFsdGh5XX0gZWxzZSB7cmVwKDAsMTAwKX0KICAgIG1lYW5fdHJhal9yW2ksXSA9IGlmKGxlbmd0aChpbl9yZWdlbik+MSl7IHJvd01lYW5zKGZpdHNfYl9saXN0JHJlZ2VuZXJhdGluZ1ssaW5fcmVnZW5dKX0gZWxzZSBpZihsZW5ndGgoaW5fcmVnZW4pPjApe2ZpdHNfYl9saXN0JHJlZ2VuZXJhdGluZ1ssaW5fcmVnZW5dfSBlbHNlIHtyZXAoMCwxMDApfQogICAgCiAgICBjb3JfbWVhbiA9IGMoY29yX21lYW4sIGNvcihtZWFuX3RyYWpfaFtpLF0sIG1lYW5fdHJhal9yW2ksXSkpCiAgfQogIGFsbF90ZXJtc19ub3JpYm8gPSBjYmluZChhbGxfdGVybXNfbm9yaWJvLCBuaGVhbHRoeSwgbnJlZ2VuLCBjb3JoZWFsdGh5LCBjb3JyZWdlbiwgY29yX21lYW4pCiAgcm93bmFtZXMobWVhbl90cmFqX2gpID0gcm93bmFtZXMobWVhbl90cmFqX3IpID0gYWxsX3Rlcm1zX25vcmlibyREZXNjcmlwdGlvbgogIAogIHRlcm1zX2xpc3RbW25dXSA9IGFsbF90ZXJtc19ub3JpYm8KfQpgYGAKCgoKCgoKCmBgYHtyfQpjbCA9IGhjbHVzdChkaXN0KHQoZGlmZl9maXRzKSksIG1ldGhvZCA9ICJ3YXJkLkQiKQpjdCA9IGN1dHJlZShjbCwgMSkKCnRlcm1fbGlzdCA9IGxpc3QoKQpmb3IoaSBpbiB1bmlxdWUoY3QpKXsKICBmc2VsID0gZnVuY3Rpb24oeCkgcmV0dXJuKHg8MC4yNSAmIG5hbWVzKGdlbmVzKSAlaW4lIG5hbWVzKGN0KVtjdD09aV0pCiAgZ2VuZXMgPSBoZXBfY29yX2ZpdF9saXN0JGhlYWx0aHlfcmVnZW5lcmF0aW5nJFguLmkuLgogIG5hbWVzKGdlbmVzKSA9IGhlcF9jb3JfZml0X2xpc3QkaGVhbHRoeV9yZWdlbmVyYXRpbmdbLDFdCiAgZ2VuZXMgPSBnZW5lc1shaXMubmEoZ2VuZXMpXQogIAogIGVnID0gY2x1c3RlclByb2ZpbGVyOjpiaXRyKG5hbWVzKGdlbmVzW2ZzZWwoZ2VuZXMpXSksIGZyb21UeXBlPSJTWU1CT0wiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b1R5cGU9IkVOVFJFWklEIiwgT3JnRGI9Im9yZy5Icy5lZy5kYiIpCiAgZWdfYWxsID0gY2x1c3RlclByb2ZpbGVyOjpiaXRyKG5hbWVzKGdlbmVzKSwgZnJvbVR5cGU9IlNZTUJPTCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvVHlwZT0iRU5UUkVaSUQiLCBPcmdEYj0ib3JnLkhzLmVnLmRiIikKICAKICB4IDwtIFJlYWN0b21lUEE6OmVucmljaFBhdGh3YXkoZ2VuZT1lZyRFTlRSRVpJRCxwdmFsdWVDdXRvZmY9MC4wNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYWRhYmxlPVQsIHVuaXZlcnNlID0gZWdfYWxsJEVOVFJFWklEKQogIAogIHRlcm1fbGlzdFtbcGFzdGUwKCJyZWFjXyIsaSldXSA9IHgKICAKICBlZ28zIDwtIGNsdXN0ZXJQcm9maWxlcjo6ZW5yaWNoR08oZ2VuZSA9IGVnJEVOVFJFWklELCB1bml2ZXJzZSA9IGVnX2FsbCRFTlRSRVpJRCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9yZ0RiID0gb3JnLkhzLmVnLmRiLCBvbnQgPSAiQlAiLCBwdmFsdWVDdXRvZmYgID0gMC4wMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXZhbHVlQ3V0b2ZmICA9IDAuMDUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwQWRqdXN0TWV0aG9kID0gIkJIIiwgcmVhZGFibGUgPSBUKQogIAogIHRlcm1fbGlzdFtbcGFzdGUwKCJnb18iLGkpXV0gPSBlZ28zCn0KYGBgCgoKCgpgYGB7cn0KZ2VuZXMgPSBoZXBfY29yX2ZpdF9saXN0JGhlYWx0aHlfcmVnZW5lcmF0aW5nJFguLmkuLgpuYW1lcyhnZW5lcykgPSBoZXBfY29yX2ZpdF9saXN0JGhlYWx0aHlfcmVnZW5lcmF0aW5nWywxXQpnZW5lcyA9IGdlbmVzWyFpcy5uYShnZW5lcyldCgojIEdPIGVucmljaG1lbnQKZnNlbCA9IGZ1bmN0aW9uKHgpIHJldHVybih4PDAuMjUgJiBuYW1lcyhnZW5lcykgJWluJSBnZykKCnNhbXBsZUdPZGF0YSA8LSBuZXcoInRvcEdPZGF0YSIsCiAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb24gPSAidGVzdCIsIG9udG9sb2d5ID0gIkJQIiwKICAgICAgICAgICAgICAgICAgICBhbGxHZW5lcyA9IGdlbmVzLCBnZW5lU2VsID0gZnNlbCwKICAgICAgICAgICAgICAgICAgICBub2RlU2l6ZSA9IDUsIGFubm90ID0gYW5uRlVOLm9yZywgCiAgICAgICAgICAgICAgICAgICAgbWFwcGluZyA9ICJvcmcuSHMuZWcuZGIiLCBJRCA9ICJzeW1ib2wiKQoKcmVzdWx0S1MuZWxpbSA8LSBydW5UZXN0KHNhbXBsZUdPZGF0YSwgYWxnb3JpdGhtID0gImVsaW0iLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRpc3RpYyA9ICJmaXNoZXIiKQoKcmVzR08gPSBHZW5UYWJsZShvYmplY3QgPSBzYW1wbGVHT2RhdGEsIGVsaW1LUyA9IHJlc3VsdEtTLmVsaW0sCiAgICAgICAgICAgICAgICAgdG9wTm9kZXMgPSByZXN1bHRLUy5lbGltQGdlbmVEYXRhWzRdLCBudW1DaGFyID0gMTAwMCkKcmVzR08kZWxpbUtTID0gc2NvcmUocmVzdWx0S1MuZWxpbSlbcmVzR08kR08uSURdCgoKIyBnZXQgZ2VuZXMKcmVsZXZhbnRfZ2VuZXMgPSBzYW1wbGVHT2RhdGFAYWxsR2VuZXNbc2FtcGxlR09kYXRhQGdlbmVTZWxlY3Rpb25GdW4oc2FtcGxlR09kYXRhQGFsbFNjb3JlcyldCmdvR2VuZXMgPSB0KGRhdGEuZnJhbWUobGFwcGx5KGdlbmVzSW5UZXJtKHNhbXBsZUdPZGF0YSksIGZ1bmN0aW9uKHgpIHBhc3RlKHhbeCAlaW4lIHJlbGV2YW50X2dlbmVzXSwgY29sbGFwc2UgPSAiLCAiKSkpKQpyb3duYW1lcyhnb0dlbmVzKSA9IGdzdWIoIi4iLCAiOiIsIHJvd25hbWVzKGdvR2VuZXMpLCBmaXhlZCA9IFQpCmNvbG5hbWVzKGdvR2VuZXMpID0gImdlbmVzIgpyZXNHTyA9IG1lcmdlKHJlc0dPLCBnb0dlbmVzLCBieS54ID0gMSwgYnkueSA9IDAsIGFsbC54ID0gVCkKc3VicmVzR08gPSByZXNHT1shKGdyZXBsKCJSUEwiLCByZXNHTyRnZW5lcykgJiBncmVwbCgiUlBTIiwgcmVzR08kZ2VuZXMpKSxdCmBgYAoKYGBge3J9CnNpZ19yZXNHTyA9IHJlc0dPW3Jlc0dPJGVsaW1LUzw9MC4wNSxdCmhwcm9mX2dvID0gbGlzdCgpCnJwcm9mX2dvID0gbGlzdCgpCmZvcihsIGluIDE6bnJvdyhzaWdfcmVzR08pKXsKICBnZW5lc19nbyA9IHVubGlzdChzdHJzcGxpdChhcy5jaGFyYWN0ZXIoc2lnX3Jlc0dPW2wsImdlbmVzIl0pLCAiLCAiKSkKICAKICBocHJvZl9nb1tbc2lnX3Jlc0dPW2wsMl1dXSA9IHQoaGVwX2ZpdHMkaGVhbHRoeVssZ2VuZXNfZ29dKQogIHJwcm9mX2dvW1tzaWdfcmVzR09bbCwyXV1dID0gdChoZXBfZml0cyRyZWdlbmVyYXRpbmdbLGdlbmVzX2dvXSkKfQpobWF4ID0gdW5saXN0KGxhcHBseShocHJvZl9nbywgZnVuY3Rpb24oeCkgd2hpY2gubWF4KGNvbFN1bXMoeCkpKSkKcm1heCA9IHVubGlzdChsYXBwbHkocnByb2ZfZ28sIGZ1bmN0aW9uKHgpIHdoaWNoLm1heChjb2xTdW1zKHgpKSkpCm5hbWVzKGhtYXgpID0gbmFtZXMocm1heCkgPSBzaWdfcmVzR08kVGVybQpgYGAKCmBgYHtyfQpzaWdfcmVzR08gPSBzaWdfcmVzR09bb3JkZXIoc2lnX3Jlc0dPJGVsaW1LUywgZGVjcmVhc2luZyA9IEYpLF0KZ29fc2hvcnRfbGlzdCA9IGxpc3QoKQpmb3IoaSBpbiAxOm5yb3coc2lnX3Jlc0dPKSl7CiAgZyA9IHVubGlzdChzdHJzcGxpdChhcy5jaGFyYWN0ZXIoc2lnX3Jlc0dPW2ksImdlbmVzIl0pLCAiLCAiKSkKICBpZighYW55KGcgJWluJSB1bmxpc3QoZ29fc2hvcnRfbGlzdCkpICYgbGVuZ3RoKGcpPjEpewogICAgZ29fc2hvcnRfbGlzdFtbc2lnX3Jlc0dPW2ksMl1dXSA9IGcKICB9Cn0KCnJlcyA9IG1hdHJpeCgwLCAxLCAxMDApCmZvcihuIGluIG5hbWVzKGdvX3Nob3J0X2xpc3QpKXsKICBpZihsZW5ndGgoZ29fc2hvcnRfbGlzdFtbbl1dKT4xKXsKICAgIGNsID0gaGNsdXN0KGRpc3QodChkaWZmX2ZpdHNbLGdvX3Nob3J0X2xpc3RbW25dXV0pKSwgbWV0aG9kID0gIndhcmQuRCIpCiAgICByZXMgPSByYmluZChyZXMsIHQoZGlmZl9maXRzWyxnb19zaG9ydF9saXN0W1tuXV1dKVtjbCRvcmRlcixdKQogIH0KfQpyZXMgPSByZXNbLTEsXQoKCnBoZWF0bWFwOjpwaGVhdG1hcChyZXMsIGNsdXN0ZXJfY29scyA9IEYsIGNsdXN0ZXJfcm93cyA9IEYsIGZvbnRzaXplX3JvdyA9IDgsIGdhcHNfcm93ID0gY3Vtc3VtKHVubGlzdChsYXBwbHkoZ29fc2hvcnRfbGlzdCwgbGVuZ3RoKSkpKQpgYGAKCmBgYHtyfQpzaWdfcmVzR08gPSBzaWdfcmVzR09bb3JkZXIoc2lnX3Jlc0dPJGVsaW1LUywgZGVjcmVhc2luZyA9IEYpLF0KZ29fbWVkID0gbWF0cml4KDAsIDEsIDEwMCkKZm9yKGkgaW4gMTpucm93KHNpZ19yZXNHTykpewogIGcgPSB1bmxpc3Qoc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKHNpZ19yZXNHT1tpLCJnZW5lcyJdKSwgIiwgIikpCiAgaWYobGVuZ3RoKGcpPjEpewogICAgZ29fbWVkID0gcmJpbmQoZ29fbWVkLCBhcHBseSh0KGRpZmZfZml0c1ssZ10pLCAyLCBtZWRpYW4pKQogICAgcm93bmFtZXMoZ29fbWVkKVtucm93KGdvX21lZCldID0gc2lnX3Jlc0dPW2ksMl0KICB9Cn0KZ29fbWVkID0gZ29fbWVkWy0xLF0KYGBgCgpgYGB7cn0KZWcgPSBjbHVzdGVyUHJvZmlsZXI6OmJpdHIobmFtZXMoZ2VuZXNbZnNlbChnZW5lcyldKSwgZnJvbVR5cGU9IlNZTUJPTCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICB0b1R5cGU9IkVOVFJFWklEIiwgT3JnRGI9Im9yZy5Icy5lZy5kYiIpCmVnX2FsbCA9IGNsdXN0ZXJQcm9maWxlcjo6Yml0cihuYW1lcyhnZW5lcyksIGZyb21UeXBlPSJTWU1CT0wiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9UeXBlPSJFTlRSRVpJRCIsIE9yZ0RiPSJvcmcuSHMuZWcuZGIiKQoKeCA8LSBhcy5kYXRhLmZyYW1lKFJlYWN0b21lUEE6OmVucmljaFBhdGh3YXkoZ2VuZT1lZyRFTlRSRVpJRCxwdmFsdWVDdXRvZmY9MC4wNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYWRhYmxlPVQsIHVuaXZlcnNlID0gZWdfYWxsJEVOVFJFWklEKSkKaGVhZChhcy5kYXRhLmZyYW1lKHgpKQoKZ29fbWVkID0gbWF0cml4KDAsIDEsIDEwMCkKZm9yKGkgaW4gMTpucm93KHgpKXsKICBnID0gdW5saXN0KHN0cnNwbGl0KGFzLmNoYXJhY3Rlcih4W2ksImdlbmVJRCJdKSwgIi8iKSkKICBpZihsZW5ndGgoZyk+MSl7CiAgICBnb19tZWQgPSByYmluZChnb19tZWQsIGFwcGx5KHQoZGlmZl9maXRzWyxnXSksIDIsIG1lZGlhbikpCiAgICByb3duYW1lcyhnb19tZWQpW25yb3coZ29fbWVkKV0gPSB4W2ksMl0KICB9Cn0KZ29fbWVkID0gZ29fbWVkWy0xLF0KCmdlbmVzX3JlYWMgPSBzdHJzcGxpdCh4JGdlbmVJRCwgIi8iKQpuYW1lcyhnZW5lc19yZWFjKSA9IHgkRGVzY3JpcHRpb24KZ2VuZXNfcmVhYyA9IHJlc2hhcGUyOjptZWx0KGdlbmVzX3JlYWMpCmRhdCA9IGdlbmVzX3JlYWNbIWR1cGxpY2F0ZWQoZ2VuZXNfcmVhYyR2YWx1ZSksXQpkYXQgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGRhdFssMV0sICJSZWFjIiA9IGRhdFssMl0pCnBoZWF0bWFwOjpwaGVhdG1hcCh0KGRpZmZfZml0cylbZGlmZl9nZW5lcyxdLCBjbHVzdGVyX2NvbHMgPSBGLCAKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRCIsIGZvbnRzaXplX3JvdyA9IDgsIGFubm90YXRpb25fcm93ID0gZGF0KQoKaHByb2ZfcmUgPSBsaXN0KCkKcnByb2ZfcmUgPSBsaXN0KCkKZm9yKGwgaW4gMTpucm93KHgpKXsKICBnZW5lc19nbyA9IHVubGlzdChzdHJzcGxpdChhcy5jaGFyYWN0ZXIoeFtsLCJnZW5lSUQiXSksICIvIikpCiAgCiAgaHByb2ZfcmVbW3hbbCwyXV1dID0gdChoZXBfZml0cyRoZWFsdGh5WyxnZW5lc19nb10pCiAgcnByb2ZfcmVbW3hbbCwyXV1dID0gdChoZXBfZml0cyRyZWdlbmVyYXRpbmdbLGdlbmVzX2dvXSkKfQoKCmhtZXQgPSBnZ3Bsb3QocmVzaGFwZTI6Om1lbHQodChhcHBseShocHJvZl9yZSRgTWV0YWxsb3RoaW9uZWlucyBiaW5kIG1ldGFsc2AsIDEsIHNjYWxlKSkpLCAKICAgICAgICAgICAgICBhZXMoeCA9IFZhcjIsIHkgPSB2YWx1ZSwgZ3JvdXAgPSBWYXIxLCBjb2xvdXIgPSBWYXIxKSkrCiAgZ2VvbV9saW5lKCkrbGFicyh0aXRsZSA9ICJIZWFsdGh5IC0gTWV0YWxsb3RoaW9uZWlucyBiaW5kIG1ldGFscyIpCnJtZXQgPSBnZ3Bsb3QocmVzaGFwZTI6Om1lbHQodChhcHBseShycHJvZl9yZSRgTWV0YWxsb3RoaW9uZWlucyBiaW5kIG1ldGFsc2AsIDEsIHNjYWxlKSkpLCAKICAgICAgICAgICAgICBhZXMoeCA9IFZhcjIsIHkgPSB2YWx1ZSwgZ3JvdXAgPSBWYXIxLCBjb2xvdXIgPSBWYXIxKSkrCiAgZ2VvbV9saW5lKCkrbGFicyh0aXRsZSA9ICJSZWdlbiAtIE1ldGFsbG90aGlvbmVpbnMgYmluZCBtZXRhbHMiKQpybGlwID0gZ2dwbG90KHJlc2hhcGUyOjptZWx0KHQoYXBwbHkocnByb2ZfcmUkYFBsYXNtYSBsaXBvcHJvdGVpbiByZW1vZGVsaW5nYCwgMSwgc2NhbGUpKSksIAogICAgICAgICAgICAgIGFlcyh4ID0gVmFyMiwgeSA9IHZhbHVlLCBncm91cCA9IFZhcjEsIGNvbG91ciA9IFZhcjEpKSsKICBnZW9tX2xpbmUoKStsYWJzKHRpdGxlID0gIlJlZ2VuIC0gUGxhc21hIGxpcG9wcm90ZWluIHJlbW9kZWxpbmciKQpobGlwID0gZ2dwbG90KHJlc2hhcGUyOjptZWx0KHQoYXBwbHkoaHByb2ZfcmUkYFBsYXNtYSBsaXBvcHJvdGVpbiByZW1vZGVsaW5nYCwgMSwgc2NhbGUpKSksIAogICAgICAgICAgICAgIGFlcyh4ID0gVmFyMiwgeSA9IHZhbHVlLCBncm91cCA9IFZhcjEsIGNvbG91ciA9IFZhcjEpKSsKICBnZW9tX2xpbmUoKStsYWJzKHRpdGxlID0gIkhlYWx0aHkgLSBQbGFzbWEgbGlwb3Byb3RlaW4gcmVtb2RlbGluZyIpCmNvd3Bsb3Q6OnBsb3RfZ3JpZChobWV0LCBybWV0LCBobGlwLCBybGlwLCBuY29sID0gMiwgcm93ID0gMiwgYWxpZ24gPSAiaHYiKQoKCgoKCgoKCgplZ28zIDwtIGNsdXN0ZXJQcm9maWxlcjo6ZW5yaWNoR08oZ2VuZSA9IGVnJEVOVFJFWklELCB1bml2ZXJzZSA9IGVnX2FsbCRFTlRSRVpJRCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiA9IG9yZy5Icy5lZy5kYiwgb250ID0gIkJQIiwgcHZhbHVlQ3V0b2ZmICA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdmFsdWVDdXRvZmYgID0gMC4wNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwQWRqdXN0TWV0aG9kID0gIkJIIiwgcmVhZGFibGUgPSBUKQoKCmdlbmVzX2VudCA9IG1lcmdlKGVnX2FsbCwgZ2VuZXMsIGJ5LnggPSAxLCBieS55ID0gMCkKdG1wID0gZ2VuZXNfZW50WywyXQpnZW5lc19lbnQgPSBnZW5lc19lbnRbLDNdCm5hbWVzKGdlbmVzX2VudCkgPSB0bXAKZWdvNCA8LSBjbHVzdGVyUHJvZmlsZXI6OmdzZUdPKGdlbmVMaXN0ID0gZ2VuZXNfZW50W29yZGVyKGdlbmVzX2VudCwgZGVjcmVhc2luZyA9IFQpXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiA9IG9yZy5Icy5lZy5kYiwgb250ID0gIkJQIiwgblBlcm0gPSAxMDAwLCBtaW5HU1NpemUgPSAxMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhHU1NpemUgPSA1MDAsIHB2YWx1ZUN1dG9mZiA9IDAuMDUsIHZlcmJvc2UgPSBGQUxTRSkKYGBgCgoKCgoKYGBge3J9CnBsb3RfZ2VuZXNfZml0cyA9IGNiaW5kKGhlcF9maXRzJGhlYWx0aHlbLGMoIlNBQTEiLCAiSEFNUCIsICJDWVAzQTQiLCAiR0xVTCIsICJDUFMxIiwgIkFSRzEiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgIGhlcF9maXRzJGVtYm9saXNlZFssYygiU0FBMSIsICJIQU1QIiwgIkNZUDNBNCIsICJHTFVMIiwgIkNQUzEiLCAiQVJHMSIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgaGVwX2ZpdHMkcmVnZW5lcmF0aW5nWyxjKCJTQUExIiwgIkhBTVAiLCAiQ1lQM0E0IiwgIkdMVUwiLCAiQ1BTMSIsICJBUkcxIildKQpwbG90X2dlbmVzX2ZpdHMgPSBhcHBseShwbG90X2dlbmVzX2ZpdHMsIDIsIGMpCgpjb2xuYW1lcyhwbG90X2dlbmVzX2ZpdHMpID0gcGFzdGUwKGNvbG5hbWVzKHBsb3RfZ2VuZXNfZml0cyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcChjKCJfaGVhbHRoeSIsICJfZW1ib2xpc2VkIiwgIl9yZWdlbmVyYXRpbmciKSwgZWFjaCA9IDYpKQpwbG90X2dlbmVzX2ZpdHMgPSByZXNoYXBlMjo6bWVsdChwbG90X2dlbmVzX2ZpdHMpCgpwbG90X2dlbmVzX2ZpdHMkY29uZCA9IHVubGlzdChsYXBwbHkoc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKHBsb3RfZ2VuZXNfZml0cyRWYXIyKSwgIl8iKSwgZnVuY3Rpb24oeCkgeFsyXSkpCgpwbG90X2dlbmVzX2ZpdHMkY29uZCA9IGZhY3RvcihwbG90X2dlbmVzX2ZpdHMkY29uZCwgbGV2ZWxzID0gYygiaGVhbHRoeSIsICJlbWJvbGlzZWQiLCAicmVnZW5lcmF0aW5nIikpCgpwbG90X2dlbmVzX2ZpdHMkZ2VuZXMgPSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KGFzLmNoYXJhY3RlcihwbG90X2dlbmVzX2ZpdHMkVmFyMiksICJfIiksIGZ1bmN0aW9uKHgpIHhbMV0pKQoKZ2dwbG90KHBsb3RfZ2VuZXNfZml0cywgYWVzKHggPSBWYXIxLCB5ID0gdmFsdWUsIGNvbG91ciA9IGdlbmVzKSkrZmFjZXRfd3JhcCh+Y29uZCkrZ2VvbV9saW5lKCkKYGBgCgoKYGBge3J9CmFsbF9wYXRoID0gdGVybXNfZ3JvdXBzJGFsbFtycGZpbHRlcih0ZXJtc19ncm91cHMkYWxsKSxdCmFsbF9wYXRoID0gYWxsX3BhdGhbb3JkZXIoYWxsX3BhdGgkcXZhbHVlLCBkZWNyZWFzaW5nID0gRiksXQoKdGFiX2dvID0gcmVzaGFwZTI6Om1lbHQobGFwcGx5KGRmX2NvciRnZW5lLCBmdW5jdGlvbihnKSBhbGxfcGF0aFt3aGljaChncmVwbChnLCBhbGxfcGF0aCRnZW5lSUQpICYgYWxsX3BhdGgkREI9PSJHTyBUZXJtIilbMV0sYygiRGVzY3JpcHRpb24iLCAiREIiLCAicXZhbHVlIildWzEsXSkpCnRhYl9nbyRnZW5lID0gZGZfY29yJGdlbmUKdGFiX2dvID0gdGFiX2dvWyxjKDYsMSwyLDQpXQp0YWJfZ28kZGUgPSBhcHBseShkZl9jb3JbLGMoMSwzLDQsNSldLCAxLCBmdW5jdGlvbih4KSBjb2xuYW1lcyhkZl9jb3JbLGMoMyw0LDUpXSlbd2hpY2goYXMubG9naWNhbCh4WzI6NF0pKV0pCnRhYl9nbyRkZVtuY2hhcih0YWJfZ28kZGUpPjEwXSA9IE5BCnRhYl9nbyRkZSA9IHVubGlzdCh0YWJfZ28kZGUpCnRhYl9nbyRjb3JyID0gZGZfY29yJGNvcnIKdGFiX3JlID0gcmVzaGFwZTI6Om1lbHQobGFwcGx5KGRmX2NvciRnZW5lLCBmdW5jdGlvbihnKSBhbGxfcGF0aFt3aGljaChncmVwbChnLCBhbGxfcGF0aCRnZW5lSUQpICYgYWxsX3BhdGgkREI9PSJSZWFjdG9tZSIpWzFdLGMoIkRlc2NyaXB0aW9uIiwgIkRCIiwgInF2YWx1ZSIpXVsxLF0pKQp0YWJfcmUkZ2VuZSA9IGRmX2NvciRnZW5lCnRhYl9yZSA9IHRhYl9yZVssYyg2LDEsMiw0KV0KdGFiX3JlJGRlID0gYXBwbHkoZGZfY29yWyxjKDEsMyw0LDUpXSwgMSwgZnVuY3Rpb24oeCkgY29sbmFtZXMoZGZfY29yWyxjKDMsNCw1KV0pW3doaWNoKGFzLmxvZ2ljYWwoeFsyOjRdKSldKQp0YWJfcmUkZGVbbmNoYXIodGFiX3JlJGRlKT4xMF0gPSBOQQp0YWJfcmUkZGUgPSB1bmxpc3QodGFiX3JlJGRlKQp0YWJfcmUkY29yciA9IGRmX2NvciRjb3JyCgp0YWJfYWxsID0gcmJpbmQodGFiX2dvLCB0YWJfcmUpCnRhYl9hbGwgPSB0YWJfYWxsW2NvbXBsZXRlLmNhc2VzKHRhYl9hbGwpLF0KCnh4eCA9IHRhYmxlKHRhYl9hbGwkRGVzY3JpcHRpb24sIHRhYl9hbGwkZGUpCnh4eCA9IHh4eFtyb3dTdW1zKHh4eCk+MixdCnh4eCA9IHh4eC9yb3dTdW1zKHh4eCkKVmlldyhhcHBseSh4eHgsIDEsIGZ1bmN0aW9uKHgpIGlmZWxzZShhbnkoeD4wLjUpLCBjb2xuYW1lcyh4eHgpW3g+MC41XSwgTkEpKSkKCmFhYSA9IGNiaW5kKHRhcHBseSh0YWJfYWxsJGNvcnIsIHRhYl9hbGwkRGVzY3JpcHRpb24sIGZ1bmN0aW9uKHgpIHN1bSh4Pj0wLjI1KS9sZW5ndGgoeCkpLCAKICAgICAgICAgICAgdGFwcGx5KHRhYl9hbGwkY29yciwgdGFiX2FsbCREZXNjcmlwdGlvbiwgZnVuY3Rpb24oeCkgbGVuZ3RoKHgpKSwKICAgICAgICAgICAgdGFwcGx5KHRhYl9hbGwkZGUsIHRhYl9hbGwkRGVzY3JpcHRpb24sIGZ1bmN0aW9uKHgpIG5hbWVzKHRhYmxlKHgpKVt3aGljaC5tYXgodGFibGUoeCkpXSkpCmBgYAoKQ2FsY3VsYXRlIGRpc3RhbmNlcyBmb3IgYWxsIHNpZ25pZmljYW50IGdlbmVzCgpgYGB7cn0KZm9yKGNvbmQgaW4gbmFtZXMocXZhbHNfbGlzdCkpewogIGdlbmVfZml0X3AgPSBxdmFsc19saXN0W1tjb25kXV0KICBnZW5lX2ZpdF92YWxzID0gZml0c19saXN0W1tjb25kXV0KICBnZW5lX2ZpdF92YWxzX2ZpbHQgPSB0KGdlbmVfZml0X3ZhbHNbLG5hbWVzKGdlbmVfZml0X3ApW2dlbmVfZml0X3A8PTAuMDVdXSkKICBzY2FsZV9maXQgPSB0KGFwcGx5KGdlbmVfZml0X3ZhbHNfZmlsdCwgMSwgc2NhbGUpKQogIAogICMgZGlzdAogIGlmKGZpbGUuZXhpc3RzKHBhc3RlMCgicmVzdWx0cy96b25hdGlvbl9jb25kL2R0d19kaXN0YW5jZV9lbmRfIiwgY29uZCwgIl9zaWdHZW5lcy5SRFMiKSkpewogICAgbmV4dAogIH0gZWxzZXsKICAgIGRkID0gZHR3OjpkdHdEaXN0KHNjYWxlX2ZpdCkgIyBUQUtFUyA0LjUgSE9VUlMKICAgIHNhdmVSRFMoZGQsIHBhc3RlMCgicmVzdWx0cy96b25hdGlvbl9jb25kL2R0d19kaXN0YW5jZV9lbmRfIiwgY29uZCwgIl9zaWdHZW5lcy5SRFMiKSkKICB9Cn0KYGBgCgpDbHVzdGVyIGdlbmVzIChwZXIgY29uZGl0aW9uKQoKYGBge3J9CnRvcF9uID0gMTAwMApmb3IoY29uZCBpbiBuYW1lcyhxdmFsc19saXN0KSl7CiAgcHJpbnQoY29uZCkKICBnZW5lX2ZpdF9wID0gcXZhbHNfbGlzdFtbY29uZF1dCiAgZ2VuZV9maXRfdmFscyA9IGZpdHNfbGlzdFtbY29uZF1dCiAgCiAgZGQgPSByZWFkUkRTKHBhc3RlMCgicmVzdWx0cy96b25hdGlvbl9jb25kL2R0d19kaXN0YW5jZV9lbmRfIiwgY29uZCwgIl9zaWdHZW5lcy5SRFMiKSkKICAKICBnZW5lX2ZpdF92YWxzX2ZpbHQgPSB0KGdlbmVfZml0X3ZhbHNbLG5hbWVzKGdlbmVfZml0X3ApW2dlbmVfZml0X3A8PTAuMDVdXSkKICBzY2FsZV9maXQgPSB0KGFwcGx5KGdlbmVfZml0X3ZhbHNfZmlsdCwgMSwgc2NhbGUpKQogIAogICMgc2VsZWN0IHRvcCBnZW5lcyAoMCBpZiB1c2luZyBhbGwpCiAgdG9wZ2VuZXMgPSBpZih0b3Bfbj09MCkgbmFtZXMoZ2VuZV9maXRfcCkgZWxzZSBuYW1lcyhoZWFkKGdlbmVfZml0X3Bbb3JkZXIoZ2VuZV9maXRfcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWNyZWFzaW5nID0gRildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3BfbikpCiAgCiAgY2x1c3RfZ2VuZXMgPSBoY2x1c3QoYXMuZGlzdChkZFt0b3BnZW5lcyx0b3BnZW5lc10pLCBtZXRob2QgPSAid2FyZC5EMiIpCiAgCiAgY2xzID0gY3V0cmVlKGNsdXN0X2dlbmVzLCA0KQogICNhcGU6OnBsb3QucGh5bG8oYXBlOjphcy5waHlsbyhjbHVzdF9nZW5lcyksIHRpcC5jb2xvciA9IHJhaW5ib3coMTApW2Nsc10pCiAgcGx0X2xpc3QgPSBsaXN0KCkKICBmb3IoaSBpbiB1bmlxdWUoY2xzKSl7CiAgICBzdWJfZGYgPSBzY2FsZV9maXRbbmFtZXMoY2xzKVtjbHM9PWldLF0KICAgIHBsb3RfZGYgPSByZXNoYXBlMjo6bWVsdChzdWJfZGYpCiAgICBwbHRfbGlzdFtbcGFzdGUwKCJnZW5lXyIsaSldXSA9IGdncGxvdChwbG90X2RmLCBhZXMoeCA9IFZhcjIsIHkgPSB2YWx1ZSwgZ3JvdXAgPSBWYXIxKSkrCiAgICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIpKwogICAgICBnZW9tX2xpbmUoYWxwaGEgPSAwLjEsIGNvbG91ciA9ICJncmV5NjkiKSsKICAgICAgc3RhdF9zdW1tYXJ5KGFlcyh5ID0gdmFsdWUsIGdyb3VwPTEpLCBmdW49bWVhbiwgY29sb3VyPSJyZWQiLCBnZW9tPSJsaW5lIixncm91cD0xKSsKICAgICAgbGFicyh0aXRsZSA9IHBhc3RlMCgiZ2VuZV8iLGkpKSsKICAgICAgdGhlbWVfYncoKQogIH0KICAKICBwZGYocGFzdGUwKCJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvZW5kX2dlbmVfZXhwcmVzc2lvbl9wcm9maWxlXyIsIGNvbmQsICIucGRmIiksCiAgICAgIGhlaWdodCA9IDQuNSwgd2lkdGggPSA0LjQpCiAgcHJpbnQoY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gcGx0X2xpc3QsIG5yb3cgPSAyLCBuY29sID0gMiwgYWxpZ24gPSAiaHYiKSkKICBkZXYub2ZmKCkKICAKICBkZl9wdmFsX2NsID0gbWVyZ2UoZGF0YS5mcmFtZShnZW5lX2ZpdF9wKSwgZGF0YS5mcmFtZShjbHMpLCBieSA9IDAsIGFsbCA9IFQpCiAgY29sbmFtZXMoZGZfcHZhbF9jbCkgPSBjKCJnZW5lIiwgInB2YWwiLCAiY2x1c3RlciIpCiAgCiAgd3JpdGUuY3N2KGRmX3B2YWxfY2wsIGZpbGUgPSBwYXN0ZTAoInJlc3VsdHMvem9uYXRpb25fY29uZC9lbmRfcHZhbF9jbHVzdF8iLCBjb25kLCAiLmNzdiIpLAogICAgICAgICAgICBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYpCgogIHdyaXRlLmNzdihnZW5lX2ZpdF92YWxzLCAKICAgICAgICAgICAgZmlsZSA9IHBhc3RlMCgicmVzdWx0cy96b25hdGlvbl9jb25kL2VuZF9nZW5lX2ZpdHNfIiwgY29uZCwgIi5jc3YiKSwKICAgICAgICAgICAgY29sLm5hbWVzID0gVCwgcm93Lm5hbWVzID0gRiwgcXVvdGUgPSBGKQp9CmBgYAoKQ2FsY3VsYXRlIGRpc3RhbmNlcyBmb3IgYWxsIHNpZ25pZmljYW50IGdlbmVzCgpgYGB7cn0KZm9yKGNvbmQgaW4gbmFtZXMocXZhbHNfbGlzdCkpewogIGdlbmVfZml0X3AgPSBxdmFsc19saXN0W1tjb25kXV0KICBnZW5lX2ZpdF92YWxzID0gZml0c19saXN0W1tjb25kXV0KICBnZW5lX2ZpdF92YWxzX2ZpbHQgPSB0KGdlbmVfZml0X3ZhbHNbLG5hbWVzKGdlbmVfZml0X3ApW2dlbmVfZml0X3A8PTAuMDVdXSkKICBzY2FsZV9maXQgPSB0KGFwcGx5KGdlbmVfZml0X3ZhbHNfZmlsdCwgMSwgc2NhbGUpKQogIAogICMgZGlzdAogIGlmKGZpbGUuZXhpc3RzKHBhc3RlMCgicmVzdWx0cy96b25hdGlvbl9jb25kL2R0d19kaXN0YW5jZV8iLCBjb25kLCAiX3NpZ0dlbmVzLlJEUyIpKSl7CiAgICBuZXh0CiAgfSBlbHNlewogICAgZGQgPSBkdHc6OmR0d0Rpc3Qoc2NhbGVfZml0KSAjIFRBS0VTIDQuNSBIT1VSUwogICAgc2F2ZVJEUyhkZCwgcGFzdGUwKCJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvZHR3X2Rpc3RhbmNlXyIsIGNvbmQsICJfc2lnR2VuZXMuUkRTIikpCiAgfQp9CmBgYAoKQ2x1c3RlciBnZW5lcyAocGVyIGNvbmRpdGlvbikKCmBgYHtyfQp0b3BfbiA9IDEwMDAKZm9yKGNvbmQgaW4gbmFtZXMocXZhbHNfbGlzdCkpewogIHByaW50KGNvbmQpCiAgZ2VuZV9maXRfcCA9IHF2YWxzX2xpc3RbW2NvbmRdXQogIGdlbmVfZml0X3ZhbHMgPSBmaXRzX2xpc3RbW2NvbmRdXQogIAogIGRkID0gcmVhZFJEUyhwYXN0ZTAoInJlc3VsdHMvem9uYXRpb25fY29uZC9kdHdfZGlzdGFuY2VfIiwgY29uZCwgIl9zaWdHZW5lcy5SRFMiKSkKICAKICBnZW5lX2ZpdF92YWxzX2ZpbHQgPSB0KGdlbmVfZml0X3ZhbHNbLG5hbWVzKGdlbmVfZml0X3ApW2dlbmVfZml0X3A8PTAuMDVdXSkKICBzY2FsZV9maXQgPSB0KGFwcGx5KGdlbmVfZml0X3ZhbHNfZmlsdCwgMSwgc2NhbGUpKQogIAogICMgc2VsZWN0IHRvcCBnZW5lcyAoMCBpZiB1c2luZyBhbGwpCiAgdG9wZ2VuZXMgPSBpZih0b3Bfbj09MCkgbmFtZXMoZ2VuZV9maXRfcCkgZWxzZSBuYW1lcyhoZWFkKGdlbmVfZml0X3Bbb3JkZXIoZ2VuZV9maXRfcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWNyZWFzaW5nID0gRildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3BfbikpCiAgCiAgY2x1c3RfZ2VuZXMgPSBoY2x1c3QoYXMuZGlzdChkZFt0b3BnZW5lcyx0b3BnZW5lc10pLCBtZXRob2QgPSAid2FyZC5EMiIpCiAgCiAgY2xzID0gY3V0cmVlKGNsdXN0X2dlbmVzLCA0KQogICNhcGU6OnBsb3QucGh5bG8oYXBlOjphcy5waHlsbyhjbHVzdF9nZW5lcyksIHRpcC5jb2xvciA9IHJhaW5ib3coMTApW2Nsc10pCiAgcGx0X2xpc3QgPSBsaXN0KCkKICBmb3IoaSBpbiB1bmlxdWUoY2xzKSl7CiAgICBzdWJfZGYgPSBzY2FsZV9maXRbbmFtZXMoY2xzKVtjbHM9PWldLF0KICAgIHBsb3RfZGYgPSByZXNoYXBlMjo6bWVsdChzdWJfZGYpCiAgICBwbHRfbGlzdFtbcGFzdGUwKCJnZW5lXyIsaSldXSA9IGdncGxvdChwbG90X2RmLCBhZXMoeCA9IFZhcjIsIHkgPSB2YWx1ZSwgZ3JvdXAgPSBWYXIxKSkrCiAgICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIpKwogICAgICBnZW9tX2xpbmUoYWxwaGEgPSAwLjEsIGNvbG91ciA9ICJncmV5NjkiKSsKICAgICAgc3RhdF9zdW1tYXJ5KGFlcyh5ID0gdmFsdWUsIGdyb3VwPTEpLCBmdW49bWVhbiwgY29sb3VyPSJyZWQiLCBnZW9tPSJsaW5lIixncm91cD0xKSsKICAgICAgbGFicyh0aXRsZSA9IHBhc3RlMCgiZ2VuZV8iLGkpKSsKICAgICAgdGhlbWVfYncoKQogIH0KICAKICBwZGYocGFzdGUwKCJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvaGVwX2dlbmVfZXhwcmVzc2lvbl9wcm9maWxlXyIsIGNvbmQsICIucGRmIiksCiAgICAgIGhlaWdodCA9IDQuNSwgd2lkdGggPSA0LjQpCiAgcHJpbnQoY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gcGx0X2xpc3QsIG5yb3cgPSAyLCBuY29sID0gMiwgYWxpZ24gPSAiaHYiKSkKICBkZXYub2ZmKCkKICAKICBkZl9wdmFsX2NsID0gbWVyZ2UoZGF0YS5mcmFtZShnZW5lX2ZpdF9wKSwgZGF0YS5mcmFtZShjbHMpLCBieSA9IDAsIGFsbCA9IFQpCiAgY29sbmFtZXMoZGZfcHZhbF9jbCkgPSBjKCJnZW5lIiwgInB2YWwiLCAiY2x1c3RlciIpCiAgCiAgd3JpdGUuY3N2KGRmX3B2YWxfY2wsIGZpbGUgPSBwYXN0ZTAoInJlc3VsdHMvem9uYXRpb25fY29uZC9oZXBfcHZhbF9jbHVzdF8iLCBjb25kLCAiLmNzdiIpLAogICAgICAgICAgICBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYpCgogIHdyaXRlLmNzdihnZW5lX2ZpdF92YWxzLCBmaWxlID0gcGFzdGUwKCJyZXN1bHRzL3pvbmF0aW9uX2NvbmQvaGVwX2dlbmVfZml0c18iLCBjb25kLCAiLmNzdiIpLAogICAgICAgICAgICBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYpCn0KYGBgCgo=